We did some load testing on our clustered dev CAS servers backed by
JpaTicketRegistry to investigate the performance problems others have
mentioned can be caused by DefaultTicketRegistryCleaner,
http://www.nabble.com/DefaultTicketRegistryCleaner---spikes-under-DB-load-td20200644.html
and 
http://www.nabble.com/MS-SQL-Server-backed-JPATicketRegistry-Deadlocks-td22853853.html.
 We did not experience deadlocks per se, but we did see application
hangs that prevented users from authenticating.  This is bad and we
are interested in working on solution that we can contribute back to
the core CAS codebase.

At first glance it appears a JpaTicketRegistryCleaner that does the
following would be both simple and performant:

    public JpaTicketRegistryCleaner(final EntityManagerFactory factory) {
        final EntityManager entityManager = factory.createEntityManager();
        this.deleteTGTQuery = entityManager.createQuery(
            "DELETE FROM TicketGrantingTicketImpl T " +
            "WHERE T.expired = true");
        this.deleteSTQuery = entityManager.createQuery(
            "DELETE FROM ServiceTicketImpl T " +
            "WHERE T.expired = true");
    }

    @Transactional
    public void clean() {
        this.deleteTGTQuery.executeUpdate();
        this.deleteSTQuery.executeUpdate();
    }

Unfortunately the above could not work because the expired state is
either not available in the database (service tickets) or does not
always reflect the expired state (ticket-granting ticket).  This is
primarily due to the use of the strategy pattern to allow configurable
ticket expiration policies in the AbstractTicket#isExpired() method.

I would like to suggest that a simple boolean flag for ticket
expiration state be maintained for all tickets, and that the
isExpired() method simply return the boolean value.  The
AbstractTicket#updateState() method could continue to apply
configurable ticket policy to update the expired flag, e.g.:

    protected final void updateState() {
        this.previousLastTimeUsed = this.lastTimeUsed;
        this.lastTimeUsed = System.currentTimeMillis();
        this.countOfUses++;
        this.expired = this.expirationPolicy.isExpired(this) ||
            (getGrantingTicket() != null && getGrantingTicket().isExpired()) ||
            isExpiredInternal();
    }

Note that this solution would not require changes to the ticket
interface or expiration policy design.  The benefit of this approach
is that the backing registry for tickets would have a simple flag that
could be easily queried to determine whether a ticket could be safely
deleted.  This would facilitate the use case above as well as
out-of-band SQL jobs to perform ticket cleanup.

Please consider this and provide feedback.

M

-- 
You are currently subscribed to cas-dev@lists.jasig.org as: 
arch...@mail-archive.com
To unsubscribe, change settings or access archives, see 
http://www.ja-sig.org/wiki/display/JSG/cas-dev

Reply via email to