keycloak
公開メンバ関数 | 限定公開メンバ関数 | 限定公開変数類 | 関数 | 非公開メンバ関数 | 全メンバ一覧
org.keycloak.models.cache.infinispan.CacheManager クラスabstract
org.keycloak.models.cache.infinispan.CacheManager の継承関係図
Inheritance graph
org.keycloak.models.cache.infinispan.CacheManager 連携図
Collaboration graph

公開メンバ関数

 CacheManager (Cache< String, Revisioned > cache, Cache< String, Long > revisions)
 
Cache< String, RevisionedgetCache ()
 
long getCurrentCounter ()
 
Long getCurrentRevision (String id)
 
void endRevisionBatch ()
 
Object invalidateObject (String id)
 
void addRevisioned (Revisioned object, long startupRevision)
 
void addRevisioned (Revisioned object, long startupRevision, long lifespan)
 
void clear ()
 
void addInvalidations (Predicate< Map.Entry< String, Revisioned >> predicate, Set< String > invalidations)
 
void sendInvalidationEvents (KeycloakSession session, Collection< InvalidationEvent > invalidationEvents, String eventKey)
 
void invalidationEventReceived (InvalidationEvent event)
 

限定公開メンバ関数

abstract Logger getLogger ()
 
void bumpVersion (String id)
 
abstract void addInvalidationsFromEvent (InvalidationEvent event, Set< String > invalidations)
 

限定公開変数類

final Cache< String, Long > revisions
 
final Cache< String, Revisionedcache
 
final UpdateCounter counter = new UpdateCounter()
 

関数

public< T extends Revisioned > T get (String id, Class< T > type)
 

非公開メンバ関数

Iterator< Map.Entry< String, Revisioned > > getEntryIterator (Predicate< Map.Entry< String, Revisioned >> predicate)
 

詳解

Some notes on how this works:

This implementation manages optimistic locking and version checks itself. The reason is Infinispan just does behave the way we need it to. Not saying Infinispan is bad, just that we have specific caching requirements!

This is an invalidation cache implementation and requires to caches: Cache 1 is an Invalidation Cache Cache 2 is a local-only revision number cache.

Each node in the cluster maintains its own revision number cache for each entry in the main invalidation cache. This revision cache holds the version counter for each cached entity.

Cache listeners do not receive a event if that node does not have an entry for that item. So, consider the following.

  1. Node 1 gets current counter for user. There currently isn't one as this user isn't cached.
  2. Node 1 reads user from DB
  3. Node 2 updates user
  4. Node 2 calls cache.remove(user). This does not result in an invalidation listener event to node 1!
  5. node 1 checks version counter, checks pass. Stale entry is cached.

The issue is that Node 1 doesn't have an entry for the user, so it never receives an invalidation listener event from Node 2 thus it can't bump the version. So, when node 1 goes to cache the user it is stale as the version number was never bumped.

So how is this issue fixed? here is pseudo code:

  1. Node 1 calls cacheManager.getCurrentRevision() to get the current local version counter of that User
  2. Node 1 getCurrentRevision() pulls current counter for that user
  3. Node 1 getCurrentRevision() adds a "invalidation.key.userid" to invalidation cache. Its just a marker. nothing else
  4. Node 2 update user
  5. Node 2 does a cache.remove(user) cache.remove(invalidation.key.userid)
  6. Node 1 receives invalidation event for invalidation.key.userid. Bumps the version counter for that user
  7. node 1 version check fails, it doesn't cache the user

    著者
    Bill Burke
    バージョン
    Revision
    1

構築子と解体子

◆ CacheManager()

org.keycloak.models.cache.infinispan.CacheManager.CacheManager ( Cache< String, Revisioned cache,
Cache< String, Long >  revisions 
)
inline
62  {
63  this.cache = cache;
64  this.revisions = revisions;
65  }
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59
final Cache< String, Long > revisions
Definition: CacheManager.java:58

関数詳解

◆ addInvalidations()

void org.keycloak.models.cache.infinispan.CacheManager.addInvalidations ( Predicate< Map.Entry< String, Revisioned >>  predicate,
Set< String >  invalidations 
)
inline
186  {
187  Iterator<Map.Entry<String, Revisioned>> it = getEntryIterator(predicate);
188  while (it.hasNext()) {
189  invalidations.add(it.next().getKey());
190  }
191  }
Iterator< Map.Entry< String, Revisioned > > getEntryIterator(Predicate< Map.Entry< String, Revisioned >> predicate)
Definition: CacheManager.java:193

◆ addInvalidationsFromEvent()

abstract void org.keycloak.models.cache.infinispan.CacheManager.addInvalidationsFromEvent ( InvalidationEvent  event,
Set< String >  invalidations 
)
abstractprotected

◆ addRevisioned() [1/2]

void org.keycloak.models.cache.infinispan.CacheManager.addRevisioned ( Revisioned  object,
long  startupRevision 
)
inline
132  {
133  addRevisioned(object, startupRevision, -1);
134  }
void addRevisioned(Revisioned object, long startupRevision)
Definition: CacheManager.java:132

◆ addRevisioned() [2/2]

void org.keycloak.models.cache.infinispan.CacheManager.addRevisioned ( Revisioned  object,
long  startupRevision,
long  lifespan 
)
inline
136  {
137  //startRevisionBatch();
138  String id = object.getId();
139  try {
140  //revisions.getAdvancedCache().lock(id);
141  Long rev = revisions.get(id);
142  if (rev == null) {
143  rev = counter.current();
144  revisions.put(id, rev);
145  }
146  revisions.startBatch();
147  if (!revisions.getAdvancedCache().lock(id)) {
148  if (getLogger().isTraceEnabled()) {
149  getLogger().tracev("Could not obtain version lock: {0}", id);
150  }
151  return;
152  }
153  rev = revisions.get(id);
154  if (rev == null) {
155  return;
156  }
157  if (rev > startupRevision) { // revision is ahead transaction start. Other transaction updated in the meantime. Don't cache
158  if (getLogger().isTraceEnabled()) {
159  getLogger().tracev("Skipped cache. Current revision {0}, Transaction start revision {1}", object.getRevision(), startupRevision);
160  }
161  return;
162  }
163  if (rev.equals(object.getRevision())) {
164  cache.putForExternalRead(id, object);
165  return;
166  }
167  if (rev > object.getRevision()) { // revision is ahead, don't cache
168  if (getLogger().isTraceEnabled()) getLogger().tracev("Skipped cache. Object revision {0}, Cache revision {1}", object.getRevision(), rev);
169  return;
170  }
171  // revisions cache has a lower value than the object.revision, so update revision and add it to cache
172  revisions.put(id, object.getRevision());
173  if (lifespan < 0) cache.putForExternalRead(id, object);
174  else cache.putForExternalRead(id, object, lifespan, TimeUnit.MILLISECONDS);
175  } finally {
177  }
178 
179  }
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59
long current()
Definition: UpdateCounter.java:14
final Cache< String, Long > revisions
Definition: CacheManager.java:58
void endRevisionBatch()
Definition: CacheManager.java:86
final UpdateCounter counter
Definition: CacheManager.java:60

◆ bumpVersion()

void org.keycloak.models.cache.infinispan.CacheManager.bumpVersion ( String  id)
inlineprotected
127  {
128  long next = counter.next();
129  Object rev = revisions.put(id, next);
130  }
final Cache< String, Long > revisions
Definition: CacheManager.java:58
long next()
Definition: UpdateCounter.java:18
final UpdateCounter counter
Definition: CacheManager.java:60

◆ clear()

void org.keycloak.models.cache.infinispan.CacheManager.clear ( )
inline
181  {
182  cache.clear();
183  revisions.clear();
184  }
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59
final Cache< String, Long > revisions
Definition: CacheManager.java:58

◆ endRevisionBatch()

void org.keycloak.models.cache.infinispan.CacheManager.endRevisionBatch ( )
inline
86  {
87  try {
88  revisions.endBatch(true);
89  } catch (Exception e) {
90  }
91 
92  }
final Cache< String, Long > revisions
Definition: CacheManager.java:58

◆ get()

public<T extends Revisioned> T org.keycloak.models.cache.infinispan.CacheManager.get ( String  id,
Class< T >  type 
)
inlinepackage
94  {
95  Revisioned o = (Revisioned)cache.get(id);
96  if (o == null) {
97  return null;
98  }
99  Long rev = revisions.get(id);
100  if (rev == null) {
101  if (getLogger().isTraceEnabled()) {
102  getLogger().tracev("get() missing rev {0}", id);
103  }
104  return null;
105  }
106  long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue();
107  if (rev > oRev) {
108  if (getLogger().isTraceEnabled()) {
109  getLogger().tracev("get() rev: {0} o.rev: {1}", rev.longValue(), oRev);
110  }
111  return null;
112  }
113  return o != null && type.isInstance(o) ? type.cast(o) : null;
114  }
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59
final Cache< String, Long > revisions
Definition: CacheManager.java:58

◆ getCache()

Cache<String, Revisioned> org.keycloak.models.cache.infinispan.CacheManager.getCache ( )
inline
69  {
70  return cache;
71  }
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59

◆ getCurrentCounter()

long org.keycloak.models.cache.infinispan.CacheManager.getCurrentCounter ( )
inline
73  {
74  return counter.current();
75  }
long current()
Definition: UpdateCounter.java:14
final UpdateCounter counter
Definition: CacheManager.java:60

◆ getCurrentRevision()

Long org.keycloak.models.cache.infinispan.CacheManager.getCurrentRevision ( String  id)
inline
77  {
78  Long revision = revisions.get(id);
79  if (revision == null) {
80  revision = counter.current();
81  }
82 
83  return revision;
84  }
long current()
Definition: UpdateCounter.java:14
final Cache< String, Long > revisions
Definition: CacheManager.java:58
final UpdateCounter counter
Definition: CacheManager.java:60

◆ getEntryIterator()

Iterator<Map.Entry<String, Revisioned> > org.keycloak.models.cache.infinispan.CacheManager.getEntryIterator ( Predicate< Map.Entry< String, Revisioned >>  predicate)
inlineprivate
193  {
194  return cache
195  .entrySet()
196  .stream()
197  .filter(predicate).iterator();
198  }
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59

◆ getLogger()

abstract Logger org.keycloak.models.cache.infinispan.CacheManager.getLogger ( )
abstractprotected

◆ invalidateObject()

Object org.keycloak.models.cache.infinispan.CacheManager.invalidateObject ( String  id)
inline
116  {
117  Revisioned removed = (Revisioned)cache.remove(id);
118 
119  if (getLogger().isTraceEnabled()) {
120  getLogger().tracef("Removed key='%s', value='%s' from cache", id, removed);
121  }
122 
123  bumpVersion(id);
124  return removed;
125  }
void bumpVersion(String id)
Definition: CacheManager.java:127
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59

◆ invalidationEventReceived()

void org.keycloak.models.cache.infinispan.CacheManager.invalidationEventReceived ( InvalidationEvent  event)
inline
211  {
212  Set<String> invalidations = new HashSet<>();
213 
214  addInvalidationsFromEvent(event, invalidations);
215 
216  getLogger().debugf("[%s] Invalidating %d cache items after received event %s", cache.getCacheManager().getAddress(), invalidations.size(), event);
217 
218  for (String invalidation : invalidations) {
219  invalidateObject(invalidation);
220  }
221  }
final Cache< String, Revisioned > cache
Definition: CacheManager.java:59
Object invalidateObject(String id)
Definition: CacheManager.java:116
abstract void addInvalidationsFromEvent(InvalidationEvent event, Set< String > invalidations)

◆ sendInvalidationEvents()

void org.keycloak.models.cache.infinispan.CacheManager.sendInvalidationEvents ( KeycloakSession  session,
Collection< InvalidationEvent invalidationEvents,
String  eventKey 
)
inline
201  {
202  ClusterProvider clusterProvider = session.getProvider(ClusterProvider.class);
203 
204  // Maybe add InvalidationEvent, which will be collection of all invalidationEvents? That will reduce cluster traffic even more.
205  for (InvalidationEvent event : invalidationEvents) {
206  clusterProvider.notify(eventKey, event, true, ClusterProvider.DCNotify.ALL_DCS);
207  }
208  }

メンバ詳解

◆ cache

final Cache<String, Revisioned> org.keycloak.models.cache.infinispan.CacheManager.cache
protected

◆ counter

final UpdateCounter org.keycloak.models.cache.infinispan.CacheManager.counter = new UpdateCounter()
protected

◆ revisions

final Cache<String, Long> org.keycloak.models.cache.infinispan.CacheManager.revisions
protected

このクラス詳解は次のファイルから抽出されました: