Search code examples
grailsservicesingletonstateless

How do you deal with shared data in stateless grails service


I was trying to implement a grails SearchService that indexes certain text and stores it in memory for faster lookup. In order to store this data, I was trying to use a private static property in the Service to store the data, but the property was randomly resetting values. After rereading documentation, I realized that this is likely because grails services are supposed to be stateless since the employee the singleton pattern. Still, not sure I understand how a static variable can vary. Does the JVM load separate copies of service classes per thread? Not sure I'm wrapping my head around what's happening.

Nonetheless, now that I know I can't rely on static variables to store application-wide data, what's the best approach to store and access data for use across the application, while keeping synchronization and avoiding races?


Caused by: java.lang.IllegalStateException: Method on class [TEXTSTORE] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly. at SearchService.buildIndex(SearchService.groovy:63) at SearchService$_indexAllDomains_closure2.doCall(SearchService.groovy:42) at SearchService.indexAllDomains(SearchService.groovy:41) at SearchService.$tt__rebuildIndex(SearchService.groovy:48) at SearchService.afterPropertiesSet(SearchService.groovy:35) ... 4 more


Solution

  • You seem to be a bit confused about services in Grails. There is no reason why a service (defaulting to singleton) can't have shared state. It's not uncommon for a service to populate some cached or indexed data when it is created so it can be used by multiple callers.

    Most often this is done by implementing the org.springframework.beans.factory.InitializingBean interface and making use of the afterPropertiesSet() method which is called when the service (Spring bean) has been created in the application context and all dependencies have been resolved.

    For example:

    package com.example
    
    import org.springframework.beans.factory.InitializingBean
    
    class MyExampleService implements InitializingBean {
      private List data
      def otherService
    
      void afterPropertiesSet() {
        data = otherService.doSomethingToFetchData()
      }
      // .. other stuff
    }
    

    By hooking into the lifecycle of the bean you can be fairly sure that even in development (when your service reloads because you've changed some code) it will still have the data needed.