Skip to content

Commit f86b5aa

Browse files
authored
[Issue #1146] reduce lock overhead in MainIndexFactory and SinglePointIndexFactory (#1149)
Now, getSinglePointIndex() and getMainIndex() are lock-free in most cases.
1 parent e10867a commit f86b5aa

File tree

2 files changed

+121
-84
lines changed

2 files changed

+121
-84
lines changed

pixels-common/src/main/java/io/pixelsdb/pixels/common/index/MainIndexFactory.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
import org.apache.logging.log4j.Logger;
2727

2828
import java.io.IOException;
29-
import java.util.HashMap;
3029
import java.util.Map;
3130
import java.util.ServiceLoader;
31+
import java.util.concurrent.ConcurrentHashMap;
3232

3333
import static java.util.Objects.requireNonNull;
3434

@@ -40,7 +40,7 @@
4040
public class MainIndexFactory
4141
{
4242
private static final Logger logger = LogManager.getLogger(MainIndexFactory.class);
43-
private final Map<Long, MainIndex> mainIndexImpls = new HashMap<>();
43+
private final Map<Long, MainIndex> mainIndexImpls = new ConcurrentHashMap<>();
4444
private final MainIndex.Scheme enabledScheme;
4545
/**
4646
* The providers of the enabled main index schemes.
@@ -114,16 +114,22 @@ public boolean isSchemeEnabled(MainIndex.Scheme scheme)
114114
* @return the main index instance
115115
* @throws SinglePointIndexException
116116
*/
117-
public synchronized MainIndex getMainIndex(long tableId) throws MainIndexException
117+
public MainIndex getMainIndex(long tableId) throws MainIndexException
118118
{
119-
if (mainIndexImpls.containsKey(tableId))
119+
MainIndex mainIndex = this.mainIndexImpls.get(tableId);
120+
if (mainIndex == null)
120121
{
121-
return mainIndexImpls.get(tableId);
122+
synchronized (this)
123+
{
124+
// double check to avoid redundant creation of mainIndex
125+
mainIndex = this.mainIndexImpls.get(tableId);
126+
if (mainIndex == null)
127+
{
128+
mainIndex = this.mainIndexProvider.createInstance(tableId, this.enabledScheme);
129+
this.mainIndexImpls.put(tableId, mainIndex);
130+
}
131+
}
122132
}
123-
124-
MainIndex mainIndex = this.mainIndexProvider.createInstance(tableId, enabledScheme);
125-
mainIndexImpls.put(tableId, mainIndex);
126-
127133
return mainIndex;
128134
}
129135

pixels-common/src/main/java/io/pixelsdb/pixels/common/index/SinglePointIndexFactory.java

Lines changed: 106 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030

3131
import java.io.IOException;
3232
import java.util.*;
33+
import java.util.concurrent.ConcurrentHashMap;
34+
import java.util.concurrent.ConcurrentSkipListSet;
35+
import java.util.concurrent.locks.Lock;
36+
import java.util.concurrent.locks.ReentrantLock;
3337

3438
import static com.google.common.base.Preconditions.checkArgument;
3539
import static java.util.Objects.requireNonNull;
@@ -41,9 +45,10 @@
4145
public class SinglePointIndexFactory
4246
{
4347
private static final Logger logger = LogManager.getLogger(SinglePointIndexFactory.class);
44-
private final Map<TableIndex, SinglePointIndex> singlePointIndexImpls = new HashMap<>();
45-
private final Set<SinglePointIndex.Scheme> enabledSchemes = new TreeSet<>();
46-
private final Map<Long, TableIndex> indexIdToTableIndex = new HashMap<>();
48+
private final Map<TableIndex, SinglePointIndex> singlePointIndexImpls = new ConcurrentHashMap<>();
49+
private final Set<SinglePointIndex.Scheme> enabledSchemes = new ConcurrentSkipListSet<>();
50+
private final Map<Long, TableIndex> indexIdToTableIndex = new ConcurrentHashMap<>();
51+
private final Lock lock = new ReentrantLock();
4752
/**
4853
* The providers of the enabled single point index schemes.
4954
*/
@@ -128,27 +133,36 @@ public boolean isSchemeEnabled(SinglePointIndex.Scheme scheme)
128133
* @return the single point index instance
129134
* @throws SinglePointIndexException
130135
*/
131-
public synchronized SinglePointIndex getSinglePointIndex(long tableId, long indexId) throws SinglePointIndexException
136+
public SinglePointIndex getSinglePointIndex(long tableId, long indexId) throws SinglePointIndexException
132137
{
133-
TableIndex tableIndex = indexIdToTableIndex.get(indexId);
138+
TableIndex tableIndex = this.indexIdToTableIndex.get(indexId);
134139
if (tableIndex == null)
135140
{
141+
this.lock.lock();
136142
try
137143
{
138-
io.pixelsdb.pixels.common.metadata.domain.SinglePointIndex singlePointIndex =
139-
MetadataService.Instance().getSinglePointIndex(indexId);
140-
if (singlePointIndex == null)
144+
// double check to avoid redundant tableIndex creation
145+
tableIndex = this.indexIdToTableIndex.get(indexId);
146+
if (tableIndex == null)
141147
{
142-
throw new SinglePointIndexException("single point index with id " + indexId + " does not exist");
148+
io.pixelsdb.pixels.common.metadata.domain.SinglePointIndex spi =
149+
MetadataService.Instance().getSinglePointIndex(indexId);
150+
if (spi == null)
151+
{
152+
throw new SinglePointIndexException("single point index with id " + indexId + " does not exist");
153+
}
154+
tableIndex = new TableIndex(tableId, indexId, spi.getIndexScheme(), spi.isUnique());
155+
this.indexIdToTableIndex.put(indexId, tableIndex);
143156
}
144-
tableIndex = new TableIndex(tableId, indexId, singlePointIndex.getIndexScheme(), singlePointIndex.isUnique());
145-
indexIdToTableIndex.put(indexId, tableIndex);
146157
} catch (MetadataException e)
147158
{
148159
throw new SinglePointIndexException("failed to query single point index information from metadata", e);
149160
}
161+
finally
162+
{
163+
this.lock.unlock();
164+
}
150165
}
151-
// 'synchronized' in Java is reentrant, it is fine to call the other getSinglePointIndex() from here.
152166
return getSinglePointIndex(tableIndex);
153167
}
154168

@@ -158,53 +172,68 @@ public synchronized SinglePointIndex getSinglePointIndex(long tableId, long inde
158172
* @return the single point index instance
159173
* @throws SinglePointIndexException
160174
*/
161-
public synchronized SinglePointIndex getSinglePointIndex(TableIndex tableIndex) throws SinglePointIndexException
175+
public SinglePointIndex getSinglePointIndex(TableIndex tableIndex) throws SinglePointIndexException
162176
{
163177
requireNonNull(tableIndex, "tableIndex is null");
164178
checkArgument(this.enabledSchemes.contains(tableIndex.scheme), "single point index scheme '" +
165179
tableIndex.scheme.toString() + "' is not enabled.");
166180

167-
if (!indexIdToTableIndex.containsKey(tableIndex.indexId))
168-
{
169-
indexIdToTableIndex.put(tableIndex.indexId, tableIndex);
170-
}
181+
this.indexIdToTableIndex.putIfAbsent(tableIndex.indexId, tableIndex);
171182

172-
if (singlePointIndexImpls.containsKey(tableIndex))
183+
SinglePointIndex singlePointIndex = this.singlePointIndexImpls.get(tableIndex);
184+
if (singlePointIndex == null)
173185
{
174-
return singlePointIndexImpls.get(tableIndex);
186+
this.lock.lock();
187+
try
188+
{
189+
// double check to avoid redundant creation of singlePointIndex
190+
singlePointIndex = this.singlePointIndexImpls.get(tableIndex);
191+
if (singlePointIndex == null)
192+
{
193+
singlePointIndex = this.singlePointIndexProviders.get(tableIndex.getScheme()).createInstance(
194+
tableIndex.tableId, tableIndex.indexId, tableIndex.scheme, tableIndex.isUnique());
195+
this.singlePointIndexImpls.put(tableIndex, singlePointIndex);
196+
}
197+
}
198+
finally
199+
{
200+
this.lock.unlock();
201+
}
175202
}
176-
177-
SinglePointIndex singlePointIndex = this.singlePointIndexProviders.get(tableIndex.getScheme())
178-
.createInstance(tableIndex.tableId, tableIndex.indexId, tableIndex.scheme, tableIndex.isUnique());
179-
singlePointIndexImpls.put(tableIndex, singlePointIndex);
180-
181203
return singlePointIndex;
182204
}
183205

184206
/**
185207
* Close all the opened single point index instances.
186208
* @throws IOException
187209
*/
188-
public synchronized void closeAll() throws SinglePointIndexException
210+
public void closeAll() throws SinglePointIndexException
189211
{
190-
for (TableIndex tableIndex : singlePointIndexImpls.keySet())
212+
this.lock.lock();
213+
try
191214
{
192-
try
215+
for (TableIndex tableIndex : this.singlePointIndexImpls.keySet())
193216
{
194-
SinglePointIndex removing = singlePointIndexImpls.get(tableIndex);
195-
if (removing != null)
217+
try
196218
{
197-
removing.close();
219+
SinglePointIndex removing = this.singlePointIndexImpls.get(tableIndex);
220+
if (removing != null)
221+
{
222+
removing.close();
223+
}
224+
} catch (IOException e)
225+
{
226+
throw new SinglePointIndexException(
227+
"failed to close single point index with id " + tableIndex.indexId, e);
198228
}
199229
}
200-
catch (IOException e)
201-
{
202-
throw new SinglePointIndexException(
203-
"failed to close single point index with id " + tableIndex.indexId, e);
204-
}
230+
this.singlePointIndexImpls.clear();
231+
this.indexIdToTableIndex.clear();
232+
}
233+
finally
234+
{
235+
this.lock.unlock();
205236
}
206-
singlePointIndexImpls.clear();
207-
indexIdToTableIndex.clear();
208237
}
209238

210239
/**
@@ -214,61 +243,63 @@ public synchronized void closeAll() throws SinglePointIndexException
214243
* @param closeAndRemove remove the index storage after closing if true
215244
* @throws SinglePointIndexException
216245
*/
217-
public synchronized void closeIndex(long tableId, long indexId, boolean closeAndRemove) throws SinglePointIndexException
246+
public void closeIndex(long tableId, long indexId, boolean closeAndRemove) throws SinglePointIndexException
218247
{
219-
TableIndex tableIndex = indexIdToTableIndex.remove(indexId);
220-
if (tableIndex != null)
248+
this.lock.lock();
249+
try
221250
{
222-
SinglePointIndex removed = singlePointIndexImpls.remove(tableIndex);
223-
if (removed != null)
251+
TableIndex tableIndex = this.indexIdToTableIndex.remove(indexId);
252+
if (tableIndex != null)
224253
{
225-
try
254+
SinglePointIndex removed = this.singlePointIndexImpls.remove(tableIndex);
255+
if (removed != null)
226256
{
227-
if (closeAndRemove)
257+
try
228258
{
229-
removed.closeAndRemove();
230-
}
231-
else
259+
if (closeAndRemove)
260+
{
261+
removed.closeAndRemove();
262+
} else
263+
{
264+
removed.close();
265+
}
266+
} catch (IOException e)
232267
{
233-
removed.close();
268+
throw new SinglePointIndexException(
269+
"failed to close single point index with id " + tableIndex.indexId, e);
234270
}
235-
}
236-
catch (IOException e)
271+
} else
237272
{
238-
throw new SinglePointIndexException(
239-
"failed to close single point index with id " + tableIndex.indexId, e);
273+
logger.warn("index with id {} once opened but not found", indexId);
240274
}
241-
}
242-
else
243-
{
244-
logger.warn("index with id {} once opened but not found", indexId);
245-
}
246-
}
247-
else
248-
{
249-
TableIndex tableIndex1 = new TableIndex(tableId, indexId, null, false);
250-
SinglePointIndex removed = singlePointIndexImpls.remove(tableIndex1);
251-
if (removed != null)
275+
} else
252276
{
253-
try
277+
TableIndex tableIndex1 = new TableIndex(tableId, indexId, null, false);
278+
SinglePointIndex removed = this.singlePointIndexImpls.remove(tableIndex1);
279+
if (removed != null)
254280
{
255-
if (closeAndRemove)
281+
try
256282
{
257-
removed.closeAndRemove();
258-
}
259-
else
283+
if (closeAndRemove)
284+
{
285+
removed.closeAndRemove();
286+
} else
287+
{
288+
removed.close();
289+
}
290+
} catch (IOException e)
260291
{
261-
removed.close();
292+
throw new SinglePointIndexException(
293+
"failed to close single point index with id " + tableIndex.indexId, e);
262294
}
295+
logger.warn("index with id {} is found but not opened properly", indexId);
263296
}
264-
catch (IOException e)
265-
{
266-
throw new SinglePointIndexException(
267-
"failed to close single point index with id " + tableIndex.indexId, e);
268-
}
269-
logger.warn("index with id {} is found but not opened properly", indexId);
270297
}
271298
}
299+
finally
300+
{
301+
this.lock.unlock();
302+
}
272303
}
273304

274305
public static class TableIndex

0 commit comments

Comments
 (0)