使用异步控制器和长轮询时,Hibernate会话获取过时数据

时间:2015-09-15 19:07:38

标签: spring hibernate postgresql grails asynchronous

我在Grails中遇到异步控制器问题。考虑以下控制器:

@Transactional(readOnly=true)
class RentController  {
    def myService


    UserProperties props
    def beforeInterceptor = {
        this.props = fetchUserProps() 
    }


    //..other actions

    @Transactional
    def rent(Long id) {
        //check some preconditions here, calling various service methods...
        if (!allOk) {
            render status: 403, text: 'appropriate.message.key'
            return
        }

        //now we long poll because most of the time the result will be
        //success within a couple of seconds
        AsyncContext ctx = startAsync()
        ctx.timeout = 5 * 1000 * 60 + 5000
        ctx.start {
            try {
                //wait for external service to confirm - can take a long time or even time out
                //save appropriate domain objects if successful
                //placeRental is also marked with @Transactional (if that makes any difference)
                def result = myService.placeRental() 
                if (result.success) {
                    render text:"OK", status: 200
                } else {
                    render status:400, text: "rejection.reason.${result.rejectionCode}"
                }
            } catch (Throwable t) {
                log.error "Rental process failed", t
                render text: "Rental process failed with exception ${t?.message}", status: 500
            } finally {
                ctx.complete()
            }
        }
    }
}

控制器和服务代码似乎工作正常(虽然上面的代码已经简化),但有时会导致数据库会话在过去被卡住了#39;

我们假设我有一个UserProperties个实例,其属性accountId在应用程序的其他位置从1更新为20,而rent动作在异步块中等待。由于异步块最终会以这种或那种方式终止(它可能成功,失败或超时),因此应用程序有时会使用UserProperties获得陈旧的accountId: 1实例。让我们说我刷新更新后的用户属性页面,我会看到accountId: 1每10次刷新大约1次,而剩下的时间是20 - 这是在我的开发机器上,没有其他人访问该应用程序(虽然在生产中可以观察到相同的行为)。我的连接池也有10个连接,所以我怀疑这里可能存在相关性。

其他奇怪的事情会发生 - 例如,我会从动作做出StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)这样简单的动作后得到render (UserProperties.list() as JSON) - 在响应已经呈现之后(成功地除了日志中的噪音)和尽管该行为正在使用@Transactional(readOnly=true)进行注释。

过时的会话似乎每次都没出现,到目前为止我们的解决方案是每天晚上重新启动服务器(应用程序目前用户很少),但是错误令人讨厌并且原因很难确定。我的猜测是,由于异步代码,数据库事务不会被提交或回滚,但是GORM,Spring和Hibernate有许多可能会卡住的角落和缝隙。

我们正在使用Postgres 9.4.1(开发机器上的9.2,同样的问题),Grails 2.5.0,Hibernate插件4.3.8.1,Tomcat 8,Cache插件1.1.8,Hibernate Filter插件0.3.2和审计日志插件1.0.1(显然,其他的东西,但这感觉它可能是相关的)。我的数据源配置包含:

hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = false
    cache.region.factory_class = 'org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory'
    singleSession = true
    flush.mode = 'manual'
    format_sql = true
}

1 个答案:

答案 0 :(得分:0)

Grails bug.一个讨厌的,一切似乎都没问题,直到你的应用开始在应用程序的完全无关的部分表现得很有趣。