searcher pool and tests

This commit is contained in:
lionsoul2014 2025-11-27 15:11:37 +08:00
parent a8087e06b7
commit f8a154aeda
4 changed files with 154 additions and 28 deletions

View File

@ -26,16 +26,16 @@ public class Ip2Region {
/* v6 pool */ /* v6 pool */
private final SearcherPool v6Pool; private final SearcherPool v6Pool;
public Ip2Region(Config v4Config, Config v6Config) { public Ip2Region(Config v4Config, Config v6Config) throws IOException {
this.v4Pool = new SearcherPool(v4Config); this.v4Pool = new SearcherPool(v4Config);
this.v6Pool = new SearcherPool(v6Config); this.v6Pool = new SearcherPool(v6Config);
} }
public String search(String ipString) throws InetAddressException, IOException { public String search(String ipString) throws InetAddressException, IOException, InterruptedException {
return search(Util.parseIP(ipString)); return search(Util.parseIP(ipString));
} }
public String search(byte[] ipBytes) throws InetAddressException, IOException { public String search(byte[] ipBytes) throws InetAddressException, IOException, InterruptedException {
// 1, define the pool with the input ip // 1, define the pool with the input ip
final SearcherPool pool; final SearcherPool pool;
if (ipBytes.length == 4) { if (ipBytes.length == 4) {
@ -47,21 +47,13 @@ public class Ip2Region {
} }
// 2, get a searcher from the pool // 2, get a searcher from the pool
final Searcher searcher = pool.getSearcher(); final Searcher searcher = pool.borrowSearcher();
try { try {
// 3, do the search // 3, do the search
final String region = searcher.search(ipBytes); return searcher.search(ipBytes);
return region; } finally {
} catch (InetAddressException e) { pool.returnSearcher(searcher);
// for the inet address error and we should return the searcher
throw e;
} catch (IOException e) {
// for the IOException usually means something is wrong with the read operation to the xdb file
// and we choose to keep the searcher and destory it right now
// so we will create a new one for the next search
try {searcher.close();} catch (IOException e1) {}
throw e;
} }
} }

View File

@ -5,9 +5,10 @@
package org.lionsoul.ip2region; package org.lionsoul.ip2region;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import org.lionsoul.ip2region.xdb.Searcher; import org.lionsoul.ip2region.xdb.Searcher;
@ -19,43 +20,94 @@ import org.lionsoul.ip2region.xdb.Searcher;
* Date 2025/11/21 * Date 2025/11/21
*/ */
public class SearcherPool { public class SearcherPool {
public static final int WAIT_OVERLOADED = 1;
public static final int NEW_OVERLOADED = 2;
// config instance // config instance
private final Config config; private final Config config;
// searcher pool // searcher pool
private final Queue<Searcher> pool; private final Queue<Searcher> pool;
// searcher lock // lock & conditions
private final ReentrantLock lock; private final ReentrantLock lock;
private final Condition emptyCondition;
private final Condition fullCondition;
// searcher numbers that was loaned out // searcher numbers that was loaned out
private final AtomicInteger loanCount; private int loanCount;
public SearcherPool(Config config) { public SearcherPool(Config config) throws IOException {
this(config, false); this(config, false);
} }
public SearcherPool(Config config, boolean fair) { public SearcherPool(Config config, boolean fair) throws IOException {
assert config.searchers > 0;
this.config = config; this.config = config;
this.pool = new LinkedList<>(); this.pool = new LinkedList<>();
this.lock = new ReentrantLock(fair); this.lock = new ReentrantLock(fair);
this.loanCount = new AtomicInteger(0); this.emptyCondition = this.lock.newCondition();
} this.fullCondition = this.lock.newCondition();
this.loanCount = 0;
// init the searcher pool // create the searchers
public void init() throws IOException {
for (int i = 0; i < config.searchers; i++) { for (int i = 0; i < config.searchers; i++) {
final Searcher searcher = new Searcher(config.ipVersion, config.xdbPath, config.vIndex, config.cBuffer); final Searcher searcher = new Searcher(config.ipVersion, config.xdbPath, config.vIndex, config.cBuffer);
pool.add(searcher); pool.add(searcher);
} }
} }
public Searcher getSearcher() { public Config getConfig() {
return null; return config;
}
public Searcher borrowSearcher() throws InterruptedException {
lock.lock();
try {
while (pool.isEmpty()) {
emptyCondition.await();
}
loanCount++;
return pool.poll();
} finally {
lock.unlock();
}
}
public void returnSearcher(final Searcher searcher) {
lock.lock();
try {
pool.add(searcher);
loanCount--;
emptyCondition.signal();
// check and signal the full condition.
// pool close
if (loanCount == 0) {
fullCondition.signal();
}
} finally {
lock.unlock();
}
} }
// close the searcher pool // close the searcher pool
public void close() { public void close() throws InterruptedException {
this.loanCount.set(config.searchers); lock.lock();
try {
while (loanCount > 0) {
fullCondition.wait();
}
final Iterator<Searcher> it = pool.iterator();
while (it.hasNext()) {
final Searcher searcher = it.next();
try {searcher.close();} catch (IOException e) {}
it.remove();
}
} finally {
lock.unlock();
}
} }
} }

View File

@ -29,6 +29,7 @@ public class Searcher {
private final Version version; private final Version version;
// random access file handle for file-based search // random access file handle for file-based search
private final String xdbPath;
private final RandomAccessFile handle; private final RandomAccessFile handle;
private int ioCount = 0; private int ioCount = 0;
@ -61,6 +62,7 @@ public class Searcher {
public Searcher(Version version, String dbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException { public Searcher(Version version, String dbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException {
this.version = version; this.version = version;
this.xdbPath = dbFile;
if (cBuff != null) { if (cBuff != null) {
this.handle = null; this.handle = null;
this.vectorIndex = null; this.vectorIndex = null;
@ -173,6 +175,16 @@ public class Searcher {
} }
} }
@Override public String toString() {
return String.format(
"%s->{version:%s, xdb:%s, vIndex:%s, cBuffer:%s}",
super.toString(),
version.name, xdbPath,
vectorIndex == null ? "null" : String.valueOf(vectorIndex.length),
contentBuff == null ? "null" : String.valueOf(contentBuff.length())
);
}
// --- static util function // --- static util function
public static Header loadHeader(RandomAccessFile handle) throws IOException { public static Header loadHeader(RandomAccessFile handle) throws IOException {

View File

@ -0,0 +1,70 @@
package org.lionsoul.ip2region;
import java.security.CodeSource;
import org.junit.Test;
import org.lionsoul.ip2region.xdb.Log;
import org.lionsoul.ip2region.xdb.Searcher;
public class SearcherPoolTest {
private static final Log log = Log.getLogger(ConfigTest.class).setLevel(Log.DEBUG);
public String getDataPath(String xdbFile) {
final CodeSource cs = this.getClass().getProtectionDomain().getCodeSource();
if (cs != null) {
// log.debugf("code path: %s", cs.getLocation().getPath().concat("../../../../data/"));
return cs.getLocation().getPath().concat("../../../../data/").concat(xdbFile);
} else {
return "../../../../data/".concat(xdbFile);
}
}
@Test
public void testV4SeacherPool() throws Exception {
final Config v4Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(5)
.setXdbPath(getDataPath("ip2region_v4.xdb"))
.asV4();
final String ipStr = "58.250.36.41";
final SearcherPool v4Pool = new SearcherPool(v4Config);
for (int i = 0; i < 20; i++) {
final Searcher searcher = v4Pool.borrowSearcher();
log.debugf("borrowed searcher %d: %s", i, searcher.toString());
final String region = searcher.search(ipStr);
log.debugf("search(%s)=%s", ipStr, region);
v4Pool.returnSearcher(searcher);
log.debugf("return searcher %d", i);
}
v4Pool.close();
log.debugf("v4 searcher pool closed gracefully");
}
@Test
public void testV6SeacherPool() throws Exception {
final Config v6Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(5)
.setXdbPath(getDataPath("ip2region_v6.xdb"))
.asV6();
final String ipStr = "240e:3b7:3272:d8d0:db09:c067:8d59:539e";
final SearcherPool v4Pool = new SearcherPool(v6Config);
for (int i = 0; i < 20; i++) {
final Searcher searcher = v4Pool.borrowSearcher();
log.debugf("borrowed searcher %d: %s", i, searcher.toString());
final String region = searcher.search(ipStr);
log.debugf("search(%s)=%s", ipStr, region);
v4Pool.returnSearcher(searcher);
log.debugf("return searcher %d", i);
}
v4Pool.close();
log.debugf("v6 searcher pool closed gracefully");
}
}