Skip to content

Commit

Permalink
feat: Automatic lock processing, overall reconstruction.
Browse files Browse the repository at this point in the history
  • Loading branch information
QwQ-dev committed Dec 22, 2024
1 parent 74fb0c2 commit acc04dd
Show file tree
Hide file tree
Showing 20 changed files with 651 additions and 623 deletions.
152 changes: 95 additions & 57 deletions cache/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### cache

The cache module based on memory and memory database, the part based on Memory and Caffeine has been completed.
The module is a caching solution that supports `Caffeine`'s `Cache` and `AsyncCache`,
as well as an in-memory `Redis` database implementation, offering functional programming support and automatic lock handling.

The original purpose of this module was to design a L1 cache for the `mongodb` module, but now it is **general-purpose**.

Expand All @@ -15,77 +16,114 @@ dependencies {
```

```java
public class Main {
public class CacheLauncher {
public static void main(String[] args) {
// test object
Person person = new Person();
person.setUuid(UUID.randomUUID());
person.setName("Johannes");
person.setAge(20);
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");

Person person2 = new Person();
person2.setUuid(UUID.randomUUID());
person2.setName("Johannes");
person2.setAge(45);

/*
* Redis cache example
*/
RedisCacheServiceInterface redisCache =
CacheServiceFactory.createRedisCache(config);

// get method will return object type, getWithType can specify the return type
int integer = redisCache.getWithType(
// get cache value by key
cache -> cache.getBucket("key").get(),

// if cache miss, do this, like query from database
() -> 1,

// if cacheAfterQuery is true, do this, store to cache
(cache, queryValue) -> cache.getBucket("key").set(queryValue),

// cacheAfterQuery
true
);

// create connection
MongoDBConnectionConfig mongoDBConnectionConfig =
MongoDBConnectionConfigFactory.create("test", "mongodb://localhost:27017/");
Datastore datastore = mongoDBConnectionConfig.getDatastore();
// string
String string = redisCache.getWithType(
// get cache value by key
cache -> cache.getBucket("key2").get(),

// if cache miss, do this, like query from database
() -> "qwq",

// if cacheAfterQuery is true, do this, store to cache
(cache, queryValue) -> cache.getBucket("key2").set(queryValue),

// cacheAfterQuery
true
);

// save data
datastore.save(person);
datastore.save(person2);


/*
* create memory cache service
*
* By default, MemoryCacheService uses ConcurrentHashMap, which means it is thread-safe.
* When a custom Map is passed in, thread safety is tied to the Map.
* Caffeine cache example
*
* Cache<Integer, String>
* - cache key and cache value type
*
* Cache<String, String>, String>
* - Cache value type
*/
MemoryCacheServiceInterface memoryCacheService = MemoryCacheServiceFactory.create();

// expiration settings (this is for one element, not the entire map)
ExpirationSettings expirationSettings = ExpirationSettings.of(100, TimeUnit.DAYS);

// db query
Supplier<String> dbQuery = () -> String.valueOf(
datastore.find(Person.class)
.filter(
Filters.eq("name", "Johannes"),
Filters.gt("age", 30)
)
.iterator()
.tryNext()
CacheServiceInterface<Cache<Integer, String>, String> caffeineCache =
CacheServiceFactory.createCaffeineCache();

// get
String qwq = caffeineCache.get(
// get cache value by key

cache -> cache.getIfPresent(1),
// if cache miss, do this, like query from database
() -> "qwq",

// if cacheAfterQuery is true, do this, store to cache
(cache, queryValue) -> cache.put(1, queryValue),

// cacheAfterQuery
true
);

// Testing time: 45, db query
memoryCacheService.get("testKey", dbQuery, true, expirationSettings);
// get with lock
String qwq2 = caffeineCache.get(
// get lock
cache -> new ReentrantLock(),

// Now we query again test L1 cache, Testing time: 0
memoryCacheService.get("testKey", dbQuery, true, expirationSettings);
}
}
// get cache value by key
cache -> cache.getIfPresent(1),

@Data
@Entity("persons")
class Person {
@Id
private UUID uuid;
// if cache miss, do this, like query from database
() -> "qwq",

private String name;
private int age;
}
```
// if cacheAfterQuery is true, do this, store to cache
(cache, queryValue) -> cache.put(1, queryValue),

It is very simple to use. We have created cache service classes for `Memory`, `Caffeine`-based `Cache`, and `AsyncCache`.
// cacheAfterQuery
true,

We only need to create them through the factory class to use them without manually writing the internal logic.
// lock settings
LockSettings.of(1, 1, TimeUnit.MINUTES)
);

### scalability
// thread-safe execution of something, redis cache same
caffeineCache.execute(
// get lock
cache -> new ReentrantLock(),

In-memory database level cache is already in the planning.
// do something
cache -> cache.getIfPresent(1),

For caches like L1 and L2, developers should manually nest them or directly use the L1 and L2 level caches provided by the `data` module.
// lock settings
LockSettings.of(1, 1, TimeUnit.MINUTES)
);

/*
* Although most of caffeine's methods are thread-safe,
* we can directly use getCache() to operate these methods.
*/
caffeineCache.getCache().put(2, "hi");
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
@FairyLaunch
@InjectableComponent
public class CacheLauncher extends Plugin {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package me.qwqdev.library.cache.factory;

import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Cache;
import lombok.experimental.UtilityClass;
import me.qwqdev.library.cache.service.CacheServiceInterface;
import me.qwqdev.library.cache.service.caffeine.CaffeineAsyncCacheService;
import me.qwqdev.library.cache.service.caffeine.CaffeineCacheService;
import me.qwqdev.library.cache.service.redis.RedisCacheService;
import me.qwqdev.library.cache.service.redis.RedisCacheServiceInterface;
import org.redisson.config.Config;

/**
* Factory for creating cache service instances.
*
* <p>Provides centralized creation of different cache implementations
* with various configuration options.
*
* @author qwq-dev
* @since 2024-12-21 20:10
*/
@UtilityClass
public final class CacheServiceFactory {
/**
* Creates a Redis cache service with the specified configuration.
*
* @param config the Redis configuration
* @return a new Redis cache service instance
*/
public static RedisCacheServiceInterface createRedisCache(Config config) {
return new RedisCacheService(config);
}

/**
* Creates a Caffeine synchronous cache service with custom configuration.
*
* @param cache the Caffeine cache
* @param <K> the cache key type
* @param <V> the cache value type
* @return a new Caffeine cache service instance
*/
public static <K, V> CacheServiceInterface<Cache<K, V>, V> createCaffeineCache(Cache<K, V> cache) {
return new CaffeineCacheService<>(cache);
}

/**
* Creates a Caffeine synchronous cache service with default configuration.
*
* @param <K> the cache key type
* @param <V> the cache value type
* @return a new Caffeine cache service instance
*/
public static <K, V> CacheServiceInterface<Cache<K, V>, V> createCaffeineCache() {
return new CaffeineCacheService<>();
}

/**
* Creates a Caffeine asynchronous cache service with custom configuration.
*
* @param asyncCache the Caffeine async cache
* @param <K> the cache key type
* @param <V> the cache value type
* @return a new async Caffeine cache service instance
*/
public static <K, V> CacheServiceInterface<AsyncCache<K, V>, V> createCaffeineAsyncCache(AsyncCache<K, V> asyncCache) {
return new CaffeineAsyncCacheService<>(asyncCache);
}

/**
* Creates a Caffeine asynchronous cache service with default configuration.
*
* @param <K> the cache key type
* @param <V> the cache value type
* @return a new async Caffeine cache service instance
*/
public static <K, V> CacheServiceInterface<AsyncCache<K, V>, V> createCaffeineAsyncCache() {
return new CaffeineAsyncCacheService<>();
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit acc04dd

Please sign in to comment.