如果在 springboot 中的结果为 merged/combined,是否有可能缓存方法(使用咖啡因)return 部分响应?
Is it possible cached methods(using caffeine) return partial response if merged/combined in the result in springboot?
我有一个用例,我配置了多个具有不同属性的缓存管理器,以及用单独的缓存名称注释的不同方法。
缓存的方法从 http 客户端异步检索数据,并缓存响应。在上述用例中,来自两个缓存方法的数据在返回结果之前被合并。有时,结果仅包含来自其中一种缓存方法的数据,刷新后问题就解决了。
我无法理解问题是在什么情况下提出的?
@Configuraions
public class CacheConfig{
public static final String CACHE1 = "cache1";
public static final String CACHE2 = "cache2";
@Value("${cache.caffeineSpec:expireAfterWrite=43200s,maximumSize=1000,recordStats}")
private String cacheSpec1;
@Value("${cache.caffeineSpec: expireAfterWrite=3600s,maximumSize=2000,recordStats}")
private String cacheSpec2;
@Bean("cacheManager1")
@Primary
public CacheManager brokerDetailscacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE1);
cacheManager.setCaffeine(Caffeine.from(cacheSpec1));
return cacheManager;
}
@Bean("cacheManager2")
public CacheManager brokerTierCodeMapCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE2, BROKER_TIER_CACHE);
cacheManager.setCaffeine(Caffeine.from(cacheSpec2));
return cacheManager;
}
}
正在使用的模型
public class Person {
private String firstname;
private String lastname;
private List<Address> adresses;
}
private class Address {
private String street;
private String City
private String zip;
}
private class PersonInfo {
private String firstname;
private String lastname;
private Address address;
}
缓存的方法class是:
@Service
@RequiredArgsConstructor
public class PersonCache {
private final DataClient dataClient;
@Cacheable(cacheNames = CacheConfig.CACHE1, cacheManager = "cacheManager1" ,sync = true)
public Map<String, Person> getPersonDetails(String firstname) {
Map<String, Person> personMap = new HashMap()<>;
//Key is first name, grouping all results by firstname
try {
personMap = dataClient.getPersonDetails(firstname)
.toCompletableFuture()
.get(3, TimeUnit.SECONDS);
}catch(Exception e) {
log.error("Error fetching response from api". e);
}
}
@Cacheable(cacheNames = CacheConfig.CACHE2, cacheManager = "cacheManager2" ,sync = true)
public Map<String, Person> getPersonDetails(String firstname) {
List<PersonInfo> personMap = new ArrayList();
try {
personMap = dataClient.getPersonInfoDetails(firstname)
.toCompletableFuture()
.get(3, TimeUnit.SECONDS);
}catch(Exception e) {
log.error("Error fetching response from api". e);
}
return transformPersonInfoToPerson(personMap);
}
}
调用方式:
@Service
@RequiredArgsConstructor
public class PersonService {
private final PersonCache personCache;
public List<Person> getPersonDetails(String firstName) {
Map<String, Person> personResponse1 = personCache.getPersonDetails(firstName);
//.. after fetching for the first result set, check for a flag and call the below cache to append the data
Map<String, Person> personResponse2 = personCache.getPersonInfoDetails(firstName);
personResponse1.putAll(personResponse2);
// This when returned at times does not contain any response from personResponse1 and only contains the personResponse2 data
return personResponse1.values();
}
}
是否有可能是异步 API 调用导致某种未命中,并且将第二个缓存的结果集添加到结果中并返回?
(调用方法也是从控制器异步调用的class)
无论触发端点的次数如何,我应该如何处理以获得一致的响应?
缓存键和值一旦进入缓存就应该被视为 immutable。这是因为它们可用于多个线程,因此之后更改条目可能变得不可预测table。当以不安全的方式完成时,该行为鲜为人知。
在您的代码中,缓存值作为 HashMap personResponse1
返回。然后对其进行修改以包含 personResponse2
的条目。充其量这会将所有内容存储在 response1 中以供下一次调用,但它也可能导致损坏,因为多个线程不同步地写入其中。当损坏时,可能无法再次找到某些条目,例如在调整大小时,它们没有正确地重新散列到正确的 table 位置,或者不再位于 bin 的链接列表中。另一种可能性是,由于值的 mutable 视图返回给未显示的客户端代码,可能该代码在处理它时删除了条目。实际行为变得不可预测table,这就是为什么它在刷新后短时间内看起来正确的原因,因为缓存丢弃了损坏的结果。
一个好的做法是存储 immutable Map.copy
或用 Collections.unmodifiableMap
装饰。然后不允许任何突变,你会立即发现这一点。使用缓存的响应时,将它们合并到新地图中。很可能您的代码未缓存,因此没有存储和共享响应,但在此处添加缓存改变了这一点,因此您现在需要注意 mutable 共享状态出现的问题。
我有一个用例,我配置了多个具有不同属性的缓存管理器,以及用单独的缓存名称注释的不同方法。 缓存的方法从 http 客户端异步检索数据,并缓存响应。在上述用例中,来自两个缓存方法的数据在返回结果之前被合并。有时,结果仅包含来自其中一种缓存方法的数据,刷新后问题就解决了。 我无法理解问题是在什么情况下提出的?
@Configuraions
public class CacheConfig{
public static final String CACHE1 = "cache1";
public static final String CACHE2 = "cache2";
@Value("${cache.caffeineSpec:expireAfterWrite=43200s,maximumSize=1000,recordStats}")
private String cacheSpec1;
@Value("${cache.caffeineSpec: expireAfterWrite=3600s,maximumSize=2000,recordStats}")
private String cacheSpec2;
@Bean("cacheManager1")
@Primary
public CacheManager brokerDetailscacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE1);
cacheManager.setCaffeine(Caffeine.from(cacheSpec1));
return cacheManager;
}
@Bean("cacheManager2")
public CacheManager brokerTierCodeMapCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE2, BROKER_TIER_CACHE);
cacheManager.setCaffeine(Caffeine.from(cacheSpec2));
return cacheManager;
}
}
正在使用的模型
public class Person {
private String firstname;
private String lastname;
private List<Address> adresses;
}
private class Address {
private String street;
private String City
private String zip;
}
private class PersonInfo {
private String firstname;
private String lastname;
private Address address;
}
缓存的方法class是:
@Service
@RequiredArgsConstructor
public class PersonCache {
private final DataClient dataClient;
@Cacheable(cacheNames = CacheConfig.CACHE1, cacheManager = "cacheManager1" ,sync = true)
public Map<String, Person> getPersonDetails(String firstname) {
Map<String, Person> personMap = new HashMap()<>;
//Key is first name, grouping all results by firstname
try {
personMap = dataClient.getPersonDetails(firstname)
.toCompletableFuture()
.get(3, TimeUnit.SECONDS);
}catch(Exception e) {
log.error("Error fetching response from api". e);
}
}
@Cacheable(cacheNames = CacheConfig.CACHE2, cacheManager = "cacheManager2" ,sync = true)
public Map<String, Person> getPersonDetails(String firstname) {
List<PersonInfo> personMap = new ArrayList();
try {
personMap = dataClient.getPersonInfoDetails(firstname)
.toCompletableFuture()
.get(3, TimeUnit.SECONDS);
}catch(Exception e) {
log.error("Error fetching response from api". e);
}
return transformPersonInfoToPerson(personMap);
}
}
调用方式:
@Service
@RequiredArgsConstructor
public class PersonService {
private final PersonCache personCache;
public List<Person> getPersonDetails(String firstName) {
Map<String, Person> personResponse1 = personCache.getPersonDetails(firstName);
//.. after fetching for the first result set, check for a flag and call the below cache to append the data
Map<String, Person> personResponse2 = personCache.getPersonInfoDetails(firstName);
personResponse1.putAll(personResponse2);
// This when returned at times does not contain any response from personResponse1 and only contains the personResponse2 data
return personResponse1.values();
}
}
是否有可能是异步 API 调用导致某种未命中,并且将第二个缓存的结果集添加到结果中并返回? (调用方法也是从控制器异步调用的class)
无论触发端点的次数如何,我应该如何处理以获得一致的响应?
缓存键和值一旦进入缓存就应该被视为 immutable。这是因为它们可用于多个线程,因此之后更改条目可能变得不可预测table。当以不安全的方式完成时,该行为鲜为人知。
在您的代码中,缓存值作为 HashMap personResponse1
返回。然后对其进行修改以包含 personResponse2
的条目。充其量这会将所有内容存储在 response1 中以供下一次调用,但它也可能导致损坏,因为多个线程不同步地写入其中。当损坏时,可能无法再次找到某些条目,例如在调整大小时,它们没有正确地重新散列到正确的 table 位置,或者不再位于 bin 的链接列表中。另一种可能性是,由于值的 mutable 视图返回给未显示的客户端代码,可能该代码在处理它时删除了条目。实际行为变得不可预测table,这就是为什么它在刷新后短时间内看起来正确的原因,因为缓存丢弃了损坏的结果。
一个好的做法是存储 immutable Map.copy
或用 Collections.unmodifiableMap
装饰。然后不允许任何突变,你会立即发现这一点。使用缓存的响应时,将它们合并到新地图中。很可能您的代码未缓存,因此没有存储和共享响应,但在此处添加缓存改变了这一点,因此您现在需要注意 mutable 共享状态出现的问题。