(@NotNull String id)
| 57 | } |
| 58 | |
| 59 | public @Nullable CacheObject get(@NotNull String id) throws RocksDBException, IOException { |
| 60 | if (id.isEmpty()) |
| 61 | throw new IllegalArgumentException(); |
| 62 | |
| 63 | var value = lru.get(id); |
| 64 | if (value != null) { |
| 65 | if (!CacheObject.isNull(value)) |
| 66 | return value; |
| 67 | |
| 68 | var nullCache = (CacheObject.NullCache)value; |
| 69 | if (System.currentTimeMillis() - nullCache.CreateTime < 5 * 60 * 1000) // 5 minutes |
| 70 | return null; // null cache 不会写入RocksDb,短时间内就会允许再次尝试。 |
| 71 | |
| 72 | // remove and try load,下面的流程会浪费一次RocksDb的查询,先这样了。 |
| 73 | lru.remove(id); |
| 74 | } |
| 75 | |
| 76 | // 当Lru不命中,并且同时多个线程并发执行到这里,会执行多次decoder/loader操作。 |
| 77 | // 也就是说同一个进程对同一个数据的decoder/loader没有互斥。 |
| 78 | |
| 79 | var key = ByteBuffer.Allocate(128); |
| 80 | key.WriteString(id); |
| 81 | var bytes = db.get(RocksDatabase.getDefaultReadOptions(), key.Bytes, 0, key.WriteIndex); |
| 82 | if (bytes != null) { |
| 83 | // decoder |
| 84 | var bb = ByteBuffer.Wrap(bytes); |
| 85 | bb.ReadString(); // skip cacheId. |
| 86 | // 当出现并发get重复从db读取时,这里的getOrAdd会忽略后面读到的value,返回已经存在的。 |
| 87 | return lru.getOrAdd(id, () -> decoder.apply(id, bb)); |
| 88 | } |
| 89 | |
| 90 | // do user loader to load object. |
| 91 | value = loader.apply(id); |
| 92 | if (value != null) |
| 93 | dbSave(value); |
| 94 | else |
| 95 | value = new CacheObject.NullCache(); |
| 96 | |
| 97 | // 当出现并发get重复从db读取时,这里的getOrAdd会忽略后面读到的value,返回已经存在的。 |
| 98 | var tmpLambda = value; |
| 99 | return lru.getOrAdd(id, () -> tmpLambda); |
| 100 | } |
| 101 | |
| 102 | private void dbSave(@NotNull CacheObject value) throws RocksDBException, IOException { |
| 103 | var id = value.cacheId(); |
nothing calls this directly
no test coverage detected