<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On 31 May 2016 at 19:42, Thomas Darimont <span dir="ltr"><<a href="mailto:thomas.darimont@googlemail.com" target="_blank">thomas.darimont@googlemail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>Hello Bill,</div><div><br></div><div>I didn't say that using JPA to store event data would lead to bad performance :-)</div><div>it's just that querying and aggregating event data via JPA(QL) is a pain ... </div><div>and I didn't want to potentially pull 100k+ events into memory to compute the stats in Java. </div><div><br></div><div>I wanted to keep the processing in the Keycloak JVM as low as possible to not risk any</div><div>OOME scenarios (though one could also use batch fetching here...).</div><div><br></div><div>That's the reason why I used a view that uses database specific means to do so.</div><div><br></div><div>With that said recording events with JPA is fine IMHO - though it could be done asynchronously in the background.</div><div>My current approach for computing stats only relies on the already stored event_entity data, </div><div>which of course needs to be persisted then - currently I don't need to store anything.</div><div><br></div><div>But since the event_entity data can be deleted I'd proposed to periodically compute</div><div>aggregates based on those when a day is over. It could then compute stuff like:</div><div><br></div><div>* totalUserCount - how many users do we have in the realm?</div><div>* totalLoginCount - how many users did login on that particular day?</div><div>* totalLoginFailedCount - how many logins failed on that day?</div><div>* totalLoginBlockedCount - how many failed logins lead to blocked accounts?</div><div>* totalRegisterCount - how many users did register on that day?</div><div>* totalAbortedRegistrationsCount - how many users abandonned the registration?</div><div>- per realm per day.</div></div></blockquote><div><br></div><div>That sounds like a much better approach. Actually it could run every N minutes with the timer provider. If it only fetches the last N minutes of events it's not going to be a huge query, but probably still worth doing it in batches (1K max at the time or something?).</div><div><br></div><div>One issue here is that the timer is currently executed on all nodes, while this particular one should only run on one node.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><br></div><div>Similar stats could also be held by each keycloak instance in-memory and shared via infinispan.</div><div>Those stats could then also periodically aggregated (every 5 mins) to give information near real-time.</div></div></blockquote><div><br></div><div>Not sure that's necessary. Having the timer run periodically should be sufficient. It should definitively be a secondary requirement and possible to disable.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><br></div><div>Regarding the rare usage of this info I'd argue that the dashboard should actually be the first</div><div>page that an administrator sees when logging in to Keycloak (after initial setup of course) - </div><div>I think it gives you a lot more than just a few stats - e.g. the possibility to login </div><div>see patterns and identify potential problems this probably consulted multiple times a day by multiple</div><div>people - especially if one uses a lot of different tenants.</div></div></blockquote><div><br></div><div>I agree it should be the main page, but thousands of users could be logging-in a second, while only one or two admins login to the admin console a day. So compared to logins, it's rare.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><br></div><div>Cheers,</div><div>Thomas</div></div><div class="HOEnZb"><div class="h5"><div class="gmail_extra"><br><div class="gmail_quote">2016-05-31 6:15 GMT+02:00 Bill Burke <span dir="ltr"><<a href="mailto:bburke@redhat.com" target="_blank">bburke@redhat.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div bgcolor="#FFFFFF" text="#000000">
<p>I didn't read whole thread on this: Having a JPA event store
would be bad performance? Isn't there more than one even per
login? That means multiple DB inserts per login just to gather
stats. Stats that would be looked at rarely (once a day? once a
week? once a month?) Just something to think about.<br>
</p><div><div>
<br>
<div>On 5/29/16 4:52 PM, Thomas Darimont
wrote:<br>
</div>
</div></div><blockquote type="cite"><div><div>
<div dir="ltr">
<div>Hello group,</div>
<div><br>
</div>
<div>a few months ago I raised the feature request "Activity
dashboard" in the Keycloak JIRA.</div>
<div><a href="https://issues.jboss.org/browse/KEYCLOAK-1840" target="_blank">https://issues.jboss.org/browse/KEYCLOAK-1840</a></div>
<div><br>
</div>
<div>This weekend I gave this a spin and I think I got pretty
far with it,</div>
<div>see attached annotated screenshot.</div>
<div><br>
</div>
<div>The idea was to leverage the information from the stored
event data</div>
<div>to compute some Keycloak usage statistics over time.</div>
<div>My current prototype supports JPA (user / event) storage
provider</div>
<div>and works with postgresql but could be adapted to other
databases including MongoDB.</div>
<div><br>
</div>
<div>Since I need to compute the usage statistics based on the
event data,</div>
<div>events need to be stored and some views (3) need to be
defined to</div>
<div>make the data accessible from JPA in a generic fashion.</div>
<div><br>
</div>
<div>Since the queries are quite complex I wanted to keep them
out</div>
<div>of the code and therefore used named native queries via
orm.xml.</div>
<div>The actual queries use some database specific date/time
functions</div>
<div>that I wanted to keep out of the code - thus I created
views </div>
<div>that could be adapted for each database and provisioned via
liquibase.</div>
<div><br>
</div>
<div>The view definitions can be found here:</div>
<div><a href="https://gist.github.com/thomasdarimont/24e11be101c6ed8773f22e1defc5d66e" target="_blank">https://gist.github.com/thomasdarimont/24e11be101c6ed8773f22e1defc5d66e</a></div>
<div><br>
</div>
<div>For MongoDB one could define appropriate aggregation
framework pipelines</div>
<div>to express the same query logic.</div>
<div><br>
</div>
<div>I basically exposed the data from those views per realm via
a newly </div>
<div>introduced AnalyticsProvider interface that is accessible
via KeycloakSession.</div>
<div><br>
</div>
<div>Data from this AnalyticsProvider is then exposed as a REST
resource called "DashboardResource".</div>
<div>Data from this REST endpoint is then consumed by the admin
frontend in a new section</div>
<div>called "dashboard".</div>
<div><br>
</div>
<div>In the frontend I used basic patternfly components, e.g.:
cards & tables:</div>
<div><a href="https://rawgit.com/patternfly/patternfly/master/tests/cards.html" target="_blank">https://rawgit.com/patternfly/patternfly/master/tests/cards.html</a></div>
<div><br>
</div>
<div>For the heatmap I used <a href="http://cal-heatmap.com/#start" target="_blank">http://cal-heatmap.com/#start</a>
which is based on d3js.</div>
<div>There is also an angularjs directive that could be used as
well.</div>
<div><a href="https://github.com/shekhargulati/angular-cal-heatmap-directive" target="_blank">https://github.com/shekhargulati/angular-cal-heatmap-directive</a></div>
<div><br>
</div>
<div>The current hacky code can be found here.:</div>
<div><a href="https://github.com/thomasdarimont/keycloak/commits/poc/KEYCLOAK-1840-dashboard" target="_blank">https://github.com/thomasdarimont/keycloak/commits/poc/KEYCLOAK-1840-dashboard</a></div>
<div><br>
</div>
<div>The relevant commit is:</div>
<div><a href="https://github.com/thomasdarimont/keycloak/commit/40a7956f8e547edc148d2ddbaf27961f2a852203" target="_blank">https://github.com/thomasdarimont/keycloak/commit/40a7956f8e547edc148d2ddbaf27961f2a852203</a></div>
<div><br>
</div>
<div>The code still needs a decent amount of polishing but I
wanted to share this with</div>
<div>you guys first to see if this could make it into Keycloak
at some point.</div>
<div><br>
</div>
<div>Cheers,</div>
<div>Thomas</div>
</div>
<br>
<fieldset></fieldset>
<br>
</div></div><span><pre>_______________________________________________
keycloak-dev mailing list
<a href="mailto:keycloak-dev@lists.jboss.org" target="_blank">keycloak-dev@lists.jboss.org</a>
<a href="https://lists.jboss.org/mailman/listinfo/keycloak-dev" target="_blank">https://lists.jboss.org/mailman/listinfo/keycloak-dev</a></pre>
</span></blockquote>
<br>
</div>
<br>_______________________________________________<br>
keycloak-dev mailing list<br>
<a href="mailto:keycloak-dev@lists.jboss.org" target="_blank">keycloak-dev@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/keycloak-dev" rel="noreferrer" target="_blank">https://lists.jboss.org/mailman/listinfo/keycloak-dev</a><br></blockquote></div><br></div>
</div></div><br>_______________________________________________<br>
keycloak-dev mailing list<br>
<a href="mailto:keycloak-dev@lists.jboss.org">keycloak-dev@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/keycloak-dev" rel="noreferrer" target="_blank">https://lists.jboss.org/mailman/listinfo/keycloak-dev</a><br></blockquote></div><br></div></div>