How to Query for licensed users when have over 900 active licenses

mary.moore January 3, 2020

I asked this on someone else's question: https://community.atlassian.com/t5/Confluence-questions/How-to-query-active-users-in-Confluence/qaq-p/24532?_ga=2.208006866.1942474880.1528220541-1332829241.1528220541 but never got a response, so I'm re-asking it as it's own question now.

When we were under 500 users the below macro worked perfectly for me; however, now that we are over 900 users - the system times out. Is there a better way to get all Confluence users name and last login for audit purposes? 

## Macro title: Last Login
## Macro has a body: N
## Body processing: Selected body processing option
## Output: Selected output option
##
## Developed by: Andrew Frayling
## Date created: 11/02/2012
## Installed by: <your name>
## Macro to display the last login date of users who have access to the current space
## @noparams
#set($containerManagerClass = $content.class.forName('com.atlassian.spring.container.ContainerManager'))
#set($getInstanceMethod = $containerManagerClass.getDeclaredMethod('getInstance',null))
#set($containerManager = $getInstanceMethod.invoke(null,null))
#set($containerContext = $containerManager.containerContext)
#set($loginManager = $containerContext.getComponent('loginManager'))
#set($users = $userAccessor.getUsers())

<table class="confluenceTable">
  <tr>
    <th class="confluenceTh">User</th>
    <th class="confluenceTh">Last Successful Login</th>
  </tr>
#foreach($user in $users)
  ## list the last login date of users who can view the current space
  
    <tr>
      <td class="confluenceTd">#usernameLink($user.name)</td>
    #if (!$loginManager.getLoginInfo($user).lastSuccessfulLoginDate)
      <td class="confluenceTd" style="background-color:#ff0000"><strong>NEVER</strong></td>
    #else
      <td class="confluenceTd">$action.dateFormatter.formatDateTime($loginManager.getLoginInfo($user).lastSuccessfulLoginDate)</td>
    #end
    </tr>
  
#end
</table>

 

1 answer

1 accepted

2 votes
Answer accepted
Sven Schatter _Lively Apps_
Marketplace Partner
Marketplace Partners provide apps and integrations available on the Atlassian Marketplace that extend the power of Atlassian products.
January 4, 2020

Hi @mary.moore,

it looks like you've reached the limit of what can be reasonably achieved with User Macros. You might be able to find some short-term workarounds but sooner or later you'll hit the same or similar problems as this solution obviously doesn't scale.

A better approach would be to directly query the Confluence database for this kind of data. There's an official Atlassian article that presents some example Queries for this. Using SQL queries you are so much more flexible and it's much more performant.

To get the exact same data that your User Macro currently generates you could use this SQL:

SELECT cu.user_name,
li.successdate
FROM cwd_user cu
JOIN user_mapping um ON um.username = cu.user_name
LEFT JOIN logininfo li ON li.username = um.user_key
ORDER BY li.successdate;

If you want to display your data in Confluence you could simply use PocketQuery to do so. With a few modifications to the standard PocketQuery template you can even get the same representation as from your User Macro:

temp.png

This is the PocketQuery Template code I've used:

<table class="aui confluenceTable pocketquery-table">
<thead>
<tr>
<th class="confluenceTh">User</th>
<th class="confluenceTh">Last Successful Login</th>
</tr>
</thead>

<tbody>
#foreach ($user in $result)
<tr>
<td class="confluenceTd">#usernameLink($user.user_name)</td>
#if($user.successdate)
<td class="confluenceTd">$dateFormatter.formatDateTime($user.successdate)</td>
#else
<td class="confluenceTd" style="background-color:#ff0000"><strong>NEVER</strong></td>
#end
</tr>
#end
</tbody>
</table>

So what's the difference between row 2 (PocketQuery) and 3 (User Macro) in the image now?

  • PocketQuery uses a SQL query which is a lot faster.
  • SQL is much more flexible so once you reach 100.000 users and run into performance problems again you could easily add a line like "WHERE successdate < xxx" or "LIMIT 1000" to the statement.
  • PocketQuery has a macro option that is called "load dynamically" so even if you run into performance problems this allows you to decouple the querying process from the macro rendering / page rendering process. The page will render normally (no timeout!) and PocketQuery will execute your query in the background. Once the result is ready PocketQuery will insert it into the page. This will even work if the query took an incredible amount of time like 10 minutes or more. But that probably shouldn't even happen.

I hope this helps! :)

Best regards,
Sven

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events