Skip to content

Cross-site validation for lazy loading of offline sessions & Switch d…#9414

Merged
hmlnarik merged 1 commit intokeycloak:mainfrom
martin-kanis:9409
Feb 3, 2022
Merged

Cross-site validation for lazy loading of offline sessions & Switch d…#9414
hmlnarik merged 1 commit intokeycloak:mainfrom
martin-kanis:9409

Conversation

@martin-kanis
Copy link
Copy Markdown
Contributor

@martin-kanis martin-kanis commented Jan 6, 2022

…efault offline sessions to lazy loaded

This PR add test scenarios to model testsuite which will test offline sessions lazy loading (in Cross DC environment) and make offline sessions to be lazy loaded by default.

Closes #9409
Closes #9410

@martin-kanis martin-kanis force-pushed the 9409 branch 4 times, most recently from a4a4478 to b244987 Compare January 11, 2022 16:55
@hmlnarik hmlnarik added area/storage Indicates an issue that touches storage (change in data layout or data manipulation) area/infinispan labels Jan 13, 2022
@hmlnarik hmlnarik self-assigned this Jan 13, 2022
@martin-kanis martin-kanis force-pushed the 9409 branch 2 times, most recently from 9c00184 to 4eb70f7 Compare January 14, 2022 09:24
@martin-kanis
Copy link
Copy Markdown
Contributor Author

Documentation + release notes PR keycloak/keycloak-documentation#1362

@martin-kanis
Copy link
Copy Markdown
Contributor Author

Pipeline run:
https://master-jenkins.com/job/universal-test-pipeline-server/1167 No regression found. I got suspicious about oracle19cRAC / org.keycloak.testsuite.oauth.BackchannelLogoutTest.postBackchannelLogoutNestedBrokeringRevokeOfflineSessions but that doesn't seem to be related to my changes. The test passed in re-run https://master-jenkins.com/job/universal-test-pipeline-server/1172

@hmlnarik hmlnarik requested a review from mhajas January 17, 2022 20:21
@hmlnarik hmlnarik added this to the 17.0.0 milestone Jan 17, 2022
@hmlnarik hmlnarik added the priority/important Must be worked on very soon label Jan 17, 2022
@martin-kanis
Copy link
Copy Markdown
Contributor Author

Link to GH discussion

Copy link
Copy Markdown
Contributor

@mhajas mhajas left a comment

Choose a reason for hiding this comment

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

Thank you for the PR @martin-kanis! I added few comments/questions

Comment thread services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java Outdated
@martin-kanis martin-kanis force-pushed the 9409 branch 4 times, most recently from 6b64cd5 to 1c01acf Compare January 31, 2022 08:58
mhajas
mhajas previously approved these changes Jan 31, 2022
Copy link
Copy Markdown
Contributor

@mhajas mhajas left a comment

Choose a reason for hiding this comment

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

Thank you for the changes @martin-kanis. This looks good to me, I added one more idea though. Feel free to scratch it if you don't think it makes sense or it isn't possible. I am approving as it works this way too.

Comment on lines 54 to 56
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One more idea to this, I am not sure this could work this way, but I believe it is worth trying since it could have better performance.

Suggested change
@NamedQuery(name="findClientSessionsClientIds", query="SELECT clientSess.clientId, clientSess.externalClientId, clientSess.clientStorageProvider" +
" FROM PersistentClientSessionEntity clientSess INNER JOIN PersistentUserSessionEntity sess ON clientSess.userSessionId = sess.userSessionId " +
" WHERE sess.offline = :offline AND sess.realmId = :realmId")
@NamedQuery(name="findClientSessionsClientIds", query="SELECT clientSess.clientId, clientSess.externalClientId, clientSess.clientStorageProvider, count(clientSess)" +
" FROM PersistentClientSessionEntity clientSess INNER JOIN PersistentUserSessionEntity sess ON clientSess.userSessionId = sess.userSessionId " +
" WHERE sess.offline = :offline AND sess.realmId = :realmId" +
"GROUP BY clientSess.clientId, clientSess.externalClientId, clientSess.clientStorageProvider")

This way we could get rid of this:

closing(query.getResultStream())
                .map(row -> {
                    String clientId = row[0].toString();
                    if (clientId.equals(PersistentClientSessionEntity.EXTERNAL)) {
                        final String externalClientId = row[1].toString();
                        final String clientStorageProvider = row[2].toString();
                        clientId = new StorageId(clientStorageProvider, externalClientId).getId();
                    }
                    return clientId;
                })
                .sorted()
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

and replace it with something like this:

closing(query.getResultStream())
                .collect(Collectors.toMap(row -> {
                    String clientId = row[0].toString();
                    if (clientId.equals(PersistentClientSessionEntity.EXTERNAL)) {
                        final String externalClientId = row[1].toString();
                        final String clientStorageProvider = row[2].toString();
                        clientId = new StorageId(clientStorageProvider, externalClientId).getId();
                    }
                    return clientId;
                }, row -> (Long) row[3])); // Not sure about the exact type of row[3] so I am not sure this will work

WDYT?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it might work. I'll double check it with some tests. But what about ordering? Do we care about order of the entries in the resulting map by final clientId? If yes, we probably need ordering in Java.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm, this is a good question, I completely missed it. The previous method used HashMap for returning the result and HashMap does not preserve order so I believe the order is not needed. Also, the current order would be a little bit different as we are using StorageId that is producing id in the form f:......:....... which wasn't there before. Maybe we could get another opinion on this, whether some order is needed @hmlnarik? WDYT?

In case some order is needed, I believe it would be possible to use something like that: ORDER BY clientSess.clientId, clientSess.clientStorageProvider.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we probably don't have to care about ordering as the return type is HashMap. The method is called only from AdminRealmResource where the map is again transformed and return type is Stream<Map<String, String>>.
I also extended one test in ClientStorageTest to test the query more.

mhajas
mhajas previously approved these changes Feb 1, 2022
Copy link
Copy Markdown
Contributor

@mhajas mhajas left a comment

Choose a reason for hiding this comment

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

Thanks Martin, the changes look great!

@martin-kanis
Copy link
Copy Markdown
Contributor Author

https://master-jenkins.com/job/universal-test-pipeline-server/1187
https://master-jenkins.com/job/universal-test-pipeline-adapters/863
There are some failures in DB matrix but I don't see them related to my changes in queries. There are no recent runs which could be compared to this run.

The adapter failure is suspicious:

13:43:01,844 WARN  [org.keycloak.events] (default task-6) type=REFRESH_TOKEN_ERROR, realmId=test, clientId=offline-client, userId=b32f301d-bf0f-4b36-bd47-516120a0d595, ipAddress=127.0.0.1, error=invalid_token, grant_type=refresh_token, refresh_token_type=Offline, refresh_token_id=ad219500-a74f-41f4-850b-81a2f061cfdf, client_auth_method=client-secret
2022-02-01 14:43:03  13:43:01,848 ERROR [org.keycloak.adapters.RefreshableKeycloakSecurityContext] (http-127.0.0.1:8280-1) Refresh token failure status: 400 {"error":"invalid_grant","error_description":"Offline user session not found"}

But it can be also found in an older run https://master-jenkins.com/job/universal-test-pipeline-adapters/857/execution/node/932/log/?consoleFull so it's probably not a regression.

@hmlnarik
Copy link
Copy Markdown
Contributor

hmlnarik commented Feb 3, 2022

Copy link
Copy Markdown
Contributor

@hmlnarik hmlnarik left a comment

Choose a reason for hiding this comment

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

Well done, @martin-kanis !

@hmlnarik hmlnarik merged commit 0471ec4 into keycloak:main Feb 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/infinispan area/storage Indicates an issue that touches storage (change in data layout or data manipulation) priority/important Must be worked on very soon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Switch default offline sessions to lazy loaded Cross-site validation for lazy loading of offline sessions

3 participants