Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Next challenges

Recent achievements

  • Global
  • Personal

Recognition

  • Give kudos
  • Received
  • Given

Leaderboard

  • Global

Trophy case

Kudos (beta program)

Kudos logo

You've been invited into the Kudos (beta program) private group. Chat with others in the program, or give feedback to Atlassian.

View group

It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

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

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

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
Community showcase
Posted in Confluence

What do you think is the most *delightful* Confluence feature? Comment for a prize!

- Create your own custom emoji 🔥 - "Shake for Feedback" on mobile 📱 - An endless supply of GIFs via GIPHY 🤩 Is there anything quite as nice as a pleasant surprise? Comment below with what...

463 views 24 9
Join discussion

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you