Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RANGER-5148: Fix redundant role cache modify triggered by role versio… #538

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class RangerRoleCache {
RangerRoleCacheWrapper roleCacheWrapper;

private RangerRoleCache() {
roleCacheWrapper = new RangerRoleCacheWrapper();
RangerAdminConfig config = RangerAdminConfig.getInstance();

waitTimeInSeconds = config.getInt("ranger.admin.policy.download.cache.max.waittime.for.update", MAX_WAIT_TIME_FOR_UPDATE);
Expand All @@ -69,21 +70,42 @@ public static RangerRoleCache getInstance() {
}

public RangerRoles getLatestRangerRoleOrCached(String serviceName, RoleDBStore roleDBStore, Long lastKnownRoleVersion, Long rangerRoleVersionInDB) throws Exception {
final RangerRoles ret;
RangerRoles ret = null;

if (lastKnownRoleVersion == null || !lastKnownRoleVersion.equals(rangerRoleVersionInDB)) {
roleCacheWrapper = new RangerRoleCacheWrapper();
ret = roleCacheWrapper.getLatestRangerRoles(serviceName, roleDBStore, lastKnownRoleVersion, rangerRoleVersionInDB);
} else {
ret = null;
ret = roleCacheWrapper.getRoles();

if (roleCacheWrapper.getRolesVersion() < rangerRoleVersionInDB) {
boolean lockResult = false;
try {
lockResult = lock.tryLock(waitTimeInSeconds, TimeUnit.SECONDS);

if (lockResult) {
if (roleCacheWrapper.getRolesVersion() < rangerRoleVersionInDB) {
ret = roleCacheWrapper.getLatestRangerRoles(
serviceName, roleDBStore, lastKnownRoleVersion, rangerRoleVersionInDB);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ret should be updated in else clause, right?

if (roleCacheWrapper.getRolesVersion() < rangerRoleVersionInDB) {
  ret = roleCacheWrapper.getLatestRangerRoles(serviceName, roleDBStore, lastKnownRoleVersion, rangerRoleVersionInDB);
} else {
  ret = roleCacheWrapper.getRoles();
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable "ret" has already been initialized and the cache has been retrieved once at line 76. There is no need to retrieve the cache repeatedly, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ret initialized in line 76 is an older version, hence the if block at line 78 is entered. Consider 2 threads:

  1. thread-1: ret is initialized to version=100; acquired lock at line 81
  2. thread-2: ret is initialized to version=100; waiting for the lock at line 81
  3. thread-1: refreshes roleCacheWrapper and ret to the latest version, say 101, at line 85; releases the lock
  4. thread-2: acquires the lock, finds that roleCacheWrapper has the lastest version; skips line 85; leaves ret at version 100

The call from thread-2 will return version 100; the expected version is 101. Makes sense?

} else {
LOG.error("Could not get lock in [{}] seconds, returning cached RangerRoles and wait Queue Length:[{}], roles version:[{}]", waitTimeInSeconds, lock.getQueueLength(), roleCacheWrapper.getRolesVersion());
}

} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
LOG.error("RangerRoleCache.getLatestRangerRoles:lock got interrupted..", exception);
} finally {
if (lockResult) {
lock.unlock();
}
}
}
}

return ret;
}

private class RangerRoleCacheWrapper {
RangerRoles roles;
Long rolesVersion;
volatile RangerRoles roles;
volatile Long rolesVersion;

RangerRoleCacheWrapper() {
this.roles = null;
Expand All @@ -100,45 +122,28 @@ public Long getRolesVersion() {

public RangerRoles getLatestRangerRoles(String serviceName, RoleDBStore roleDBStore, Long lastKnownRoleVersion, Long rolesVersionInDB) throws Exception {
RangerRoles ret = null;
boolean lockResult = false;

LOG.debug("==> RangerRoleCache.getLatestRangerRoles(ServiceName= {} lastKnownRoleVersion= {} rolesVersionInDB= {})", serviceName, lastKnownRoleVersion, rolesVersionInDB);

try {
lockResult = lock.tryLock(waitTimeInSeconds, TimeUnit.SECONDS);

if (lockResult) {
// We are getting all the Roles to be downloaded for now. Should do downloades for each service based on what roles are there in the policies.
final long startTimeMs = System.currentTimeMillis();
SearchFilter searchFilter = null;
final Set<RangerRole> rolesInDB = new HashSet<>(roleDBStore.getRoles(searchFilter));
final long dbLoadTimeMs = System.currentTimeMillis() - startTimeMs;
Date updateTime = new Date();
// We are getting all the Roles to be downloaded for now. Should do downloades for each service based on what roles are there in the policies.
final long startTimeMs = System.currentTimeMillis();
SearchFilter searchFilter = null;
final Set<RangerRole> rolesInDB = new HashSet<>(roleDBStore.getRoles(searchFilter));
final long dbLoadTimeMs = System.currentTimeMillis() - startTimeMs;
Date updateTime = new Date();

if (LOG.isDebugEnabled()) {
LOG.debug("loading Roles from database and it took:{} seconds", TimeUnit.MILLISECONDS.toSeconds(dbLoadTimeMs));
}

ret = new RangerRoles();
if (LOG.isDebugEnabled()) {
LOG.debug("loading Roles from database and it took:{} seconds", TimeUnit.MILLISECONDS.toSeconds(dbLoadTimeMs));
}

ret.setRangerRoles(rolesInDB);
ret.setRoleUpdateTime(updateTime);
ret.setRoleVersion(rolesVersionInDB);
ret = new RangerRoles();

rolesVersion = rolesVersionInDB;
roles = ret;
} else {
LOG.debug("Could not get lock in [{}] seconds, returning cached RangerRoles", waitTimeInSeconds);
ret.setRangerRoles(rolesInDB);
ret.setRoleUpdateTime(updateTime);
ret.setRoleVersion(rolesVersionInDB);

ret = getRoles();
}
} catch (InterruptedException exception) {
LOG.error("RangerRoleCache.getLatestRangerRoles:lock got interrupted..", exception);
} finally {
if (lockResult) {
lock.unlock();
}
}
roles = ret;
rolesVersion = rolesVersionInDB;

LOG.debug("<== RangerRoleCache.getLatestRangerRoles(ServiceName= {} lastKnownRoleVersion= {} rolesVersionInDB= {} RangerRoles= {})", serviceName, lastKnownRoleVersion, rolesVersionInDB, ret);

Expand Down