Skip to content

Commit 39509bf

Browse files
kangelovjricher
authored andcommitted
Performance improvement of token cleanup:
an alternative token cleanup mechanism designed to maintain a very compact memory footprint while performing cleanup in consecutive runs of the cleanup thread. This serves to address OutOfMemoryException issues of the original token cleanup mechanism when process is under load. Also, added cleanup of the authentication_holder table.
1 parent fcb3ccb commit 39509bf

File tree

8 files changed

+64
-18
lines changed

8 files changed

+64
-18
lines changed

openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
@Entity
3434
@Table(name = "authentication_holder")
3535
@NamedQueries ({
36-
@NamedQuery(name = "AuthenticationHolderEntity.getByAuthentication", query = "select a from AuthenticationHolderEntity a where a.authentication = :authentication")
36+
@NamedQuery(name = "AuthenticationHolderEntity.getByAuthentication", query = "select a from AuthenticationHolderEntity a where a.authentication = :authentication"),
37+
@NamedQuery(name = "AuthenticationHolderEntity.getUnusedAuthenticationHolders", query = "select a from AuthenticationHolderEntity a where a.id not in (select t.authenticationHolderId from OAuth2AccessTokenEntity t) and a.id not in (select r.authenticationHolderId from OAuth2RefreshTokenEntity r)")
3738
})
3839
public class AuthenticationHolderEntity {
3940

openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
@Table(name = "access_token")
5959
@NamedQueries({
6060
@NamedQuery(name = "OAuth2AccessTokenEntity.getAll", query = "select a from OAuth2AccessTokenEntity a"),
61+
@NamedQuery(name = "OAuth2AccessTokenEntity.getAllExpiredByDate", query = "select a from OAuth2AccessTokenEntity a where a.expiration <= :date"),
6162
@NamedQuery(name = "OAuth2AccessTokenEntity.getByRefreshToken", query = "select a from OAuth2AccessTokenEntity a where a.refreshToken = :refreshToken"),
6263
@NamedQuery(name = "OAuth2AccessTokenEntity.getByClient", query = "select a from OAuth2AccessTokenEntity a where a.client = :client"),
6364
@NamedQuery(name = "OAuth2AccessTokenEntity.getByAuthentication", query = "select a from OAuth2AccessTokenEntity a where a.authenticationHolder.authentication = :authentication"),

openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2RefreshTokenEntity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
@Table(name = "refresh_token")
5151
@NamedQueries({
5252
@NamedQuery(name = "OAuth2RefreshTokenEntity.getAll", query = "select r from OAuth2RefreshTokenEntity r"),
53+
@NamedQuery(name = "OAuth2RefreshTokenEntity.getAllExpiredByDate", query = "select r from OAuth2RefreshTokenEntity r where r.expiration <= :date"),
5354
@NamedQuery(name = "OAuth2RefreshTokenEntity.getByClient", query = "select r from OAuth2RefreshTokenEntity r where r.client = :client"),
5455
@NamedQuery(name = "OAuth2RefreshTokenEntity.getByTokenValue", query = "select r from OAuth2RefreshTokenEntity r where r.value = :tokenValue"),
5556
@NamedQuery(name = "OAuth2RefreshTokenEntity.getByAuthentication", query = "select r from OAuth2RefreshTokenEntity r where r.authenticationHolder.authentication = :authentication")

openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
******************************************************************************/
1717
package org.mitre.oauth2.repository;
1818

19+
import java.util.List;
20+
1921
import org.mitre.oauth2.model.AuthenticationHolderEntity;
2022
import org.springframework.security.oauth2.provider.OAuth2Authentication;
2123

@@ -30,5 +32,8 @@ public interface AuthenticationHolderRepository {
3032
public void remove(AuthenticationHolderEntity a);
3133

3234
public AuthenticationHolderEntity save(AuthenticationHolderEntity a);
35+
36+
public List<AuthenticationHolderEntity> getOrphanedAuthenticationHolders();
37+
3338

3439
}

openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,9 @@ public interface OAuth2TokenRepository {
5757
public Set<OAuth2AccessTokenEntity> getAllAccessTokens();
5858

5959
public Set<OAuth2RefreshTokenEntity> getAllRefreshTokens();
60+
61+
public Set<OAuth2AccessTokenEntity> getAllExpiredAccessTokens();
62+
63+
public Set<OAuth2RefreshTokenEntity> getAllExpiredRefreshTokens();
6064

6165
}

openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
******************************************************************************/
1717
package org.mitre.oauth2.repository.impl;
1818

19+
import java.util.List;
20+
1921
import javax.persistence.EntityManager;
2022
import javax.persistence.PersistenceContext;
2123
import javax.persistence.TypedQuery;
@@ -31,6 +33,8 @@
3133
@Transactional
3234
public class JpaAuthenticationHolderRepository implements AuthenticationHolderRepository {
3335

36+
private static final int MAXEXPIREDRESULTS = 1000;
37+
3438
@PersistenceContext
3539
private EntityManager manager;
3640

@@ -73,5 +77,14 @@ public void remove(AuthenticationHolderEntity a) {
7377
public AuthenticationHolderEntity save(AuthenticationHolderEntity a) {
7478
return JpaUtil.saveOrUpdate(a.getId(), manager, a);
7579
}
80+
81+
@Override
82+
@Transactional
83+
public List<AuthenticationHolderEntity> getOrphanedAuthenticationHolders() {
84+
TypedQuery<AuthenticationHolderEntity> query = manager.createNamedQuery("AuthenticationHolderEntity.getUnusedAuthenticationHolders", AuthenticationHolderEntity.class);
85+
query.setMaxResults(MAXEXPIREDRESULTS);
86+
List<AuthenticationHolderEntity> unusedAuthenticationHolders = query.getResultList();
87+
return unusedAuthenticationHolders;
88+
}
7689

7790
}

openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
******************************************************************************/
1717
package org.mitre.oauth2.repository.impl;
1818

19+
import java.util.Date;
1920
import java.util.LinkedHashSet;
2021
import java.util.List;
2122
import java.util.Set;
@@ -36,6 +37,8 @@
3637
@Repository
3738
public class JpaOAuth2TokenRepository implements OAuth2TokenRepository {
3839

40+
private static final int MAXEXPIREDRESULTS = 1000;
41+
3942
@PersistenceContext
4043
private EntityManager manager;
4144

@@ -178,5 +181,21 @@ public OAuth2AccessTokenEntity getAccessTokenForIdToken(OAuth2AccessTokenEntity
178181
List<OAuth2AccessTokenEntity> accessTokens = queryA.getResultList();
179182
return JpaUtil.getSingleResult(accessTokens);
180183
}
184+
185+
@Override
186+
public Set<OAuth2AccessTokenEntity> getAllExpiredAccessTokens() {
187+
TypedQuery<OAuth2AccessTokenEntity> query = manager.createNamedQuery("OAuth2AccessTokenEntity.getAllExpiredByDate", OAuth2AccessTokenEntity.class);
188+
query.setParameter("date", new Date());
189+
query.setMaxResults(MAXEXPIREDRESULTS);
190+
return new LinkedHashSet<OAuth2AccessTokenEntity>(query.getResultList());
191+
}
192+
193+
@Override
194+
public Set<OAuth2RefreshTokenEntity> getAllExpiredRefreshTokens() {
195+
TypedQuery<OAuth2RefreshTokenEntity> query = manager.createNamedQuery("OAuth2RefreshTokenEntity.getAllExpiredByDate", OAuth2RefreshTokenEntity.class);
196+
query.setParameter("date", new Date());
197+
query.setMaxResults(MAXEXPIREDRESULTS);
198+
return new LinkedHashSet<OAuth2RefreshTokenEntity>(query.getResultList());
199+
}
181200

182201
}

openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -399,36 +399,38 @@ public void clearExpiredTokens() {
399399
Collection<OAuth2AccessTokenEntity> accessTokens = getExpiredAccessTokens();
400400
logger.info("Found " + accessTokens.size() + " expired access tokens");
401401
for (OAuth2AccessTokenEntity oAuth2AccessTokenEntity : accessTokens) {
402-
revokeAccessToken(oAuth2AccessTokenEntity);
402+
try {
403+
revokeAccessToken(oAuth2AccessTokenEntity);
404+
} catch (IllegalArgumentException e) {
405+
//An ID token is deleted with its corresponding access token, but then the ID token is on the list of expired tokens as well and there is
406+
//nothing in place to distinguish it from any other.
407+
//An attempt to delete an already deleted token returns an error, stopping the cleanup dead. We need it to keep going.
408+
}
403409
}
404410

405411
Collection<OAuth2RefreshTokenEntity> refreshTokens = getExpiredRefreshTokens();
406412
logger.info("Found " + refreshTokens.size() + " expired refresh tokens");
407413
for (OAuth2RefreshTokenEntity oAuth2RefreshTokenEntity : refreshTokens) {
408414
revokeRefreshToken(oAuth2RefreshTokenEntity);
409415
}
410-
}
411-
412-
private Predicate<OAuth2AccessTokenEntity> isAccessTokenExpired = new Predicate<OAuth2AccessTokenEntity>() {
413-
@Override
414-
public boolean apply(OAuth2AccessTokenEntity input) {
415-
return (input != null && input.isExpired());
416+
417+
Collection<AuthenticationHolderEntity> authHolders = getOrphanedAuthenticationHolders();
418+
logger.info("Found " + authHolders.size() + " orphaned authentication holders");
419+
for(AuthenticationHolderEntity authHolder : authHolders) {
420+
authenticationHolderRepository.remove(authHolder);
416421
}
417-
};
418-
419-
private Predicate<OAuth2RefreshTokenEntity> isRefreshTokenExpired = new Predicate<OAuth2RefreshTokenEntity>() {
420-
@Override
421-
public boolean apply(OAuth2RefreshTokenEntity input) {
422-
return (input != null && input.isExpired());
423-
}
424-
};
422+
}
425423

426424
private Collection<OAuth2AccessTokenEntity> getExpiredAccessTokens() {
427-
return Collections2.filter(tokenRepository.getAllAccessTokens(), isAccessTokenExpired);
425+
return Sets.newHashSet(tokenRepository.getAllExpiredAccessTokens());
428426
}
429427

430428
private Collection<OAuth2RefreshTokenEntity> getExpiredRefreshTokens() {
431-
return Collections2.filter(tokenRepository.getAllRefreshTokens(), isRefreshTokenExpired);
429+
return Sets.newHashSet(tokenRepository.getAllExpiredRefreshTokens());
430+
}
431+
432+
private Collection<AuthenticationHolderEntity> getOrphanedAuthenticationHolders() {
433+
return Sets.newHashSet(authenticationHolderRepository.getOrphanedAuthenticationHolders());
432434
}
433435

434436
/* (non-Javadoc)

0 commit comments

Comments
 (0)