Repository: nextflow-io/nextflow
Pull request #5833: Decouple cid store from session
Author: login: pditommaso
Created: 2025-03-01T16:29:00Z | Merged: 2025-03-03T18:54:22Z

This PR double the CID store from the Session object. Tests needs to be fixed. 


Diff:
diff --git a/modules/nextflow/src/main/groovy/nextflow/Session.groovy b/modules/nextflow/src/main/groovy/nextflow/Session.groovy
index 9e5352520b..98c7017d73 100644
--- a/modules/nextflow/src/main/groovy/nextflow/Session.groovy
+++ b/modules/nextflow/src/main/groovy/nextflow/Session.groovy
@@ -16,11 +16,6 @@
 
 package nextflow
 
-import nextflow.data.cid.CidStore
-import nextflow.data.cid.CidStoreFactory
-import nextflow.data.cid.DefaultCidStore
-import nextflow.data.config.DataConfig
-
 import java.nio.file.Files
 import java.nio.file.Path
 import java.nio.file.Paths
@@ -259,14 +254,6 @@ class Session implements ISession {
 
     private boolean statsEnabled
 
-    private volatile boolean cidEnabled
-
-    boolean getCidEnabled() { cidEnabled }
-
-    private CidStore cidStore
-
-    CidStore getCidStore() { cidStore }
-
     private WorkflowMetadata workflowMetadata
 
     private WorkflowStatsObserver statsObserver
@@ -406,11 +393,6 @@ class Session implements ISession {
         // -- file porter config
         this.filePorter = new FilePorter(this)
 
-        if(config.navigate('workflow.data')) {
-            this.cidEnabled = true
-            this.cidStore = CidStoreFactory.create(DataConfig.create(this))
-        }
-
     }
 
     protected Path cloudCachePath(Map cloudcache, Path workDir) {
diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdCid.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdCid.groovy
index 53476e0277..bdf5943806 100644
--- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdCid.groovy
+++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdCid.groovy
@@ -26,6 +26,7 @@ import nextflow.config.ConfigBuilder
 import nextflow.dag.MermaidHtmlRenderer
 import nextflow.data.cid.CidHistoryFile
 import nextflow.data.cid.CidStore
+import nextflow.data.cid.CidStoreFactory
 import nextflow.data.cid.model.DataType
 import nextflow.exception.AbortOperationException
 import nextflow.plugin.Plugins
@@ -112,7 +113,12 @@ class CmdCid extends CmdBase {
                     .setBaseDir(Paths.get('.'))
                     .build()
             final session = new Session(config)
-            printHistory(session.cidStore)
+            final store = CidStoreFactory.getOrCreate(session)
+            if (store) {
+                printHistory(store)
+            } else {
+                println "Error CID store not loaded. Check Nextflow configuration."
+            }
         }
 
         private void printHistory(CidStore store) {
@@ -157,12 +163,15 @@ class CmdCid extends CmdBase {
                     .setOptions(getLauncher().getOptions())
                     .setBaseDir(Paths.get('.'))
                     .build()
-            final session = new Session(config)
-            final store = session.cidStore
-            try {
-                println store.load(key).toString()
-            }catch (Throwable e){
-                println "Error loading ${args[0]}."
+            final store = CidStoreFactory.getOrCreate(new Session(config))
+            if (store) {
+                try {
+                    println store.load(key).toString()
+                } catch (Throwable e) {
+                    println "Error loading ${args[0]}."
+                }
+            } else {
+                println "Error CID store not loaded. Check Nextflow configuration."
             }
         }
 
@@ -196,8 +205,7 @@ class CmdCid extends CmdBase {
                     .setOptions(getLauncher().getOptions())
                     .setBaseDir(Paths.get('.'))
                     .build()
-                final session = new Session(config)
-                final store = session.cidStore
+                final store = CidStoreFactory.getOrCreate(new Session(config))
                 final template = readTemplate()
                 final network = getLineage(store, args[0])
                 Path file = Path.of(args[1])
diff --git a/modules/nextflow/src/main/groovy/nextflow/data/cid/CidObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/data/cid/CidObserver.groovy
index 4a52b2ee75..578678fbe4 100644
--- a/modules/nextflow/src/main/groovy/nextflow/data/cid/CidObserver.groovy
+++ b/modules/nextflow/src/main/groovy/nextflow/data/cid/CidObserver.groovy
@@ -65,9 +65,9 @@ class CidObserver implements TraceObserver {
     private WorkflowResults workflowResults
     private Map<String,String> outputsStoreDirCid = new HashMap<String,String>(10)
 
-    CidObserver(Session session){
+    CidObserver(Session session, CidStore store){
         this.session = session
-        this.store = session.cidStore
+        this.store = store
     }
 
     @Override
diff --git a/modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy b/modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
index b48a0fa963..1592f36928 100644
--- a/modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
+++ b/modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
@@ -19,8 +19,10 @@ package nextflow.data.cid
 
 import groovy.transform.CompileStatic
 import groovy.util.logging.Slf4j
+import nextflow.Session
 import nextflow.data.config.DataConfig
 import nextflow.plugin.Plugins
+import nextflow.util.TestOnly
 import org.pf4j.ExtensionPoint
 
 /**
@@ -32,9 +34,13 @@ import org.pf4j.ExtensionPoint
 @CompileStatic
 abstract class CidStoreFactory implements ExtensionPoint {
 
+    private static CidStore instance
+
+    private static boolean initialized
+
     protected abstract CidStore newInstance(DataConfig config)
 
-    static CidStore create(DataConfig config){
+    private static CidStore create(DataConfig config){
         final all = Plugins.getPriorityExtensions(CidStoreFactory)
         if( !all )
             throw new IllegalStateException("Unable to find Nextflow CID store factory")
@@ -43,4 +49,26 @@ abstract class CidStoreFactory implements ExtensionPoint {
         return factory.newInstance(config)
     }
 
+    static CidStore getOrCreate(Session session) {
+        if( instance || initialized )
+            return instance
+        synchronized (CidStoreFactory.class) {
+            if( instance || initialized )
+                return instance
+            initialized = true
+            final config = DataConfig.create(session)
+            if( !config.enabled )
+                return null
+            return instance = create(config)
+        }
+    }
+
+    @TestOnly
+    static void reset(){
+        synchronized (CidStoreFactory.class) {
+            instance = null
+            initialized = false
+        }
+    }
+
 }
diff --git a/modules/nextflow/src/main/groovy/nextflow/data/config/DataConfig.groovy b/modules/nextflow/src/main/groovy/nextflow/data/config/DataConfig.groovy
index 467598218e..7b5f0687dd 100644
--- a/modules/nextflow/src/main/groovy/nextflow/data/config/DataConfig.groovy
+++ b/modules/nextflow/src/main/groovy/nextflow/data/config/DataConfig.groovy
@@ -20,6 +20,7 @@ package nextflow.data.config
 import groovy.transform.CompileStatic
 import nextflow.Global
 import nextflow.Session
+import nextflow.util.TestOnly
 
 /**
  * Model workflow data config
@@ -31,8 +32,11 @@ class DataConfig {
 
     final DataStoreOpts store
 
+    final boolean enabled
+
     DataConfig(Map opts) {
         this.store = new DataStoreOpts(opts.store as Map ?: Map.of())
+        this.enabled = opts.enabled as boolean ?: false
     }
 
     static Map<String,Object> asMap() {
diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/DefaultObserverFactory.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/DefaultObserverFactory.groovy
index dd57c4168d..ce1782ffc4 100644
--- a/modules/nextflow/src/main/groovy/nextflow/trace/DefaultObserverFactory.groovy
+++ b/modules/nextflow/src/main/groovy/nextflow/trace/DefaultObserverFactory.groovy
@@ -4,6 +4,7 @@ import java.nio.file.Path
 
 import nextflow.Session
 import nextflow.data.cid.CidObserver
+import nextflow.data.cid.CidStoreFactory
 
 /**
  * Creates Nextflow observes object
@@ -26,14 +27,14 @@ class DefaultObserverFactory implements TraceObserverFactory {
         createTimelineObserver(result)
         createDagObserver(result)
         createAnsiLogObserver(result)
-        if( session.cidEnabled ){
-            createCidObserver(result)
-        }
+        createCidObserver(result)
         return result
     }
 
     protected void createCidObserver(Collection<TraceObserver> result) {
-        result.add( new CidObserver(this.session) )
+        final store = CidStoreFactory.getOrCreate(session)
+        if( store )
+            result.add( new CidObserver(this.session, store) )
     }
 
     protected void createAnsiLogObserver(Collection<TraceObserver> result) {
diff --git a/modules/nextflow/src/test/groovy/nextflow/cli/CmdCidTest.groovy b/modules/nextflow/src/test/groovy/nextflow/cli/CmdCidTest.groovy
index 774a5cd63a..d833a34e9c 100644
--- a/modules/nextflow/src/test/groovy/nextflow/cli/CmdCidTest.groovy
+++ b/modules/nextflow/src/test/groovy/nextflow/cli/CmdCidTest.groovy
@@ -17,6 +17,7 @@
 package nextflow.cli
 
 import groovy.json.JsonOutput
+import nextflow.data.cid.CidStoreFactory
 
 import java.nio.file.Files
 
@@ -27,6 +28,7 @@ import org.junit.Rule
 import spock.lang.Specification
 import test.OutputCapture
 
+
 /**
  * CLI cid Tests
  *
@@ -36,7 +38,13 @@ class CmdCidTest extends Specification {
 
     def cleanup() {
         Plugins.stop()
+        CidStoreFactory.reset()
+    }
+
+    def setupSpec() {
+        CidStoreFactory.reset()
     }
+
     /*
      * Read more http://mrhaki.blogspot.com.es/2015/02/spocklight-capture-and-assert-system.html
      */
@@ -45,9 +53,9 @@ class CmdCidTest extends Specification {
 
     def 'should print executions cids' (){
         given:
-            def folder = Files.createTempDirectory('test')
+            def folder = Files.createTempDirectory('test').toAbsolutePath()
             def configFile = folder.resolve('nextflow.config')
-            configFile.text = "workflow.data.store.location = '$folder'".toString()
+            configFile.text = "workflow.data.enabled = true\nworkflow.data.store.location = '$folder'".toString()
             def historyFile = folder.resolve(".meta/.history")
             Files.createDirectories(historyFile.parent)
             def uniqueId = UUID.randomUUID()
@@ -71,15 +79,15 @@ class CmdCidTest extends Specification {
             stdout.size() == 2
             stdout[1] == recordEntry
 
-        cleanup:
-            folder?.deleteDir()
+        //cleanup:
+            //folder?.deleteDir()
     }
 
     def 'should print no history' (){
         given:
-        def folder = Files.createTempDirectory('test')
+        def folder = Files.createTempDirectory('test').toAbsolutePath()
         def configFile = folder.resolve('nextflow.config')
-        configFile.text = "workflow.data.store.location = '$folder'".toString()
+        configFile.text = "workflow.data.enabled = true\nworkflow.data.store.location = '$folder'".toString()
         def historyFile = folder.resolve(".meta/.history")
         Files.createDirectories(historyFile.parent)
         def launcher = Mock(Launcher){
@@ -105,9 +113,9 @@ class CmdCidTest extends Specification {
 
     def 'should show cid content' (){
         given:
-        def folder = Files.createTempDirectory('test')
+        def folder = Files.createTempDirectory('test').toAbsolutePath()
         def configFile = folder.resolve('nextflow.config')
-        configFile.text = "workflow.data.store.location = '$folder'".toString()
+        configFile.text = "workflow.data.enabled = true\nworkflow.data.store.location = '$folder'".toString()
         def cidFile = folder.resolve(".meta/12345/.data.json")
         Files.createDirectories(cidFile.parent)
         def launcher = Mock(Launcher){
@@ -143,9 +151,9 @@ class CmdCidTest extends Specification {
 
     def 'should warn if no cid content' (){
         given:
-        def folder = Files.createTempDirectory('test')
+        def folder = Files.createTempDirectory('test').toAbsolutePath()
         def configFile = folder.resolve('nextflow.config')
-        configFile.text = "workflow.data.store.location = '$folder'".toString()
+        configFile.text = "workflow.data.enabled = true\nworkflow.data.store.location = '$folder'".toString()
         def launcher = Mock(Launcher){
             getOptions() >> new CliOptions(config: [configFile.toString()])
         }
@@ -170,10 +178,10 @@ class CmdCidTest extends Specification {
 
     def 'should get lineage cid content' (){
         given:
-        def folder = Files.createTempDirectory('test')
+        def folder = Files.createTempDirectory('test').toAbsolutePath()
         def configFile = folder.resolve('nextflow.config')
         def outputHtml = folder.resolve('lineage.html')
-        configFile.text = "workflow.data.store.location = '$folder'".toString()
+        configFile.text = "workflow.data.enabled = true\nworkflow.data.store.location = '$folder'".toString()
         def launcher = Mock(Launcher){
             getOptions() >> new CliOptions(config: [configFile.toString()])
         }
@@ -245,14 +253,9 @@ class CmdCidTest extends Specification {
         outputHtml.exists()
         outputHtml.text == expectedOutput
 
-
         cleanup:
         folder?.deleteDir()
-    }
-
-
-
-
 
+    }
 
 }
diff --git a/modules/nextflow/src/test/groovy/nextflow/data/cid/CidObserverTest.groovy b/modules/nextflow/src/test/groovy/nextflow/data/cid/CidObserverTest.groovy
index 3167350cca..3d678638a7 100644
--- a/modules/nextflow/src/test/groovy/nextflow/data/cid/CidObserverTest.groovy
+++ b/modules/nextflow/src/test/groovy/nextflow/data/cid/CidObserverTest.groovy
@@ -47,7 +47,7 @@ class CidObserverTest extends Specification {
     def 'should save workflow' (){
         given:
         def folder = Files.createTempDirectory('test')
-        def config = [workflow:[data:[store:[location:folder.toString()]]]]
+        def config = [workflow:[data:[enabled: true, store:[location:folder.toString()]]]]
         def store = new DefaultCidStore();
         def uniqueId = UUID.randomUUID()
         def scriptFile = folder.resolve("main.nf")
@@ -61,14 +61,13 @@ class CidObserverTest extends Specification {
         }
         def session = Mock(Session) {
             getConfig() >> config
-            getCidStore() >> store
             getUniqueId() >> uniqueId
             getRunName() >> "test_run"
             getWorkflowMetadata() >> metadata
             getParams() >> new ScriptBinding.ParamsMap()
         }
         store.open(DataConfig.create(session))
-        def observer = new CidObserver(session)
+        def observer = new CidObserver(session, store)
         def expectedString = '{"type":"WorkflowRun","workflow":{"type": "Workflow",' +
             '"mainScriptFile":{"path":"file://' + scriptFile.toString() + '", "checksum": "78910"},' +
             '"otherScriptFiles": [], "repository": "https://nextflow.io/nf-test/",' +
@@ -88,17 +87,16 @@ class CidObserverTest extends Specification {
     def 'should save task run' () {
         given:
         def folder = Files.createTempDirectory('test')
-        def config = [workflow:[data:[store:[location:folder.toString()]]]]
+        def config = [workflow:[data:[enabled: true, store:[location:folder.toString()]]]]
         def store = new DefaultCidStore();
         def uniqueId = UUID.randomUUID()
         def session = Mock(Session) {
             getConfig()>>config
-            getCidStore()>>store
             getUniqueId()>>uniqueId
             getRunName()>>"test_run"
         }
         store.open(DataConfig.create(session))
-        def observer = new CidObserver(session)
+        def observer = new CidObserver(session, store)
         and:
         def hash = HashCode.fromInt(123456789)
         and:
@@ -136,14 +134,13 @@ class CidObserverTest extends Specification {
     def 'should save task output' () {
         given:
         def folder = Files.createTempDirectory('test')
-        def config = [workflow:[data:[store:[location:folder.toString()]]]]
+        def config = [workflow:[data:[enabled: true, store:[location:folder.toString()]]]]
         def store = new DefaultCidStore();
         def session = Mock(Session) {
             getConfig()>>config
-            getCidStore()>>store
         }
         store.open(DataConfig.create(session))
-        def observer = Spy(new CidObserver(session))
+        def observer = Spy(new CidObserver(session, store))
         and:
         def workDir = folder.resolve('12/34567890')
         Files.createDirectories(workDir)
@@ -187,11 +184,10 @@ class CidObserverTest extends Specification {
 
     def 'should relativise task output dirs' (){
         when:
-        def config = [workflow:[data:[store:[location:'cid']]]]
+        def config = [workflow:[data:[enabled: true, store:[location:'cid']]]]
         def store = new DefaultCidStore();
         def session = Mock(Session) {
             getConfig()>>config
-            getCidStore()>>store
         }
         def hash = HashCode.fromInt(123456789)
         def taskConfig = Mock(TaskConfig){
@@ -205,7 +201,7 @@ class CidObserverTest extends Specification {
             getConfig() >> taskConfig
         }
         store.open(DataConfig.create(session))
-        def observer = new CidObserver(session)
+        def observer = new CidObserver(session, store)
         then:
         observer.getTaskRelative(task, PATH) == EXPECTED
         where:
@@ -220,11 +216,10 @@ class CidObserverTest extends Specification {
 
     def 'should return exception when relativize task output dirs' (){
         when:
-            def config = [workflow:[data:[store:[location:'cid']]]]
+            def config = [workflow:[data:[enabled: true, store:[location:'cid']]]]
             def store = new DefaultCidStore();
             def session = Mock(Session) {
                 getConfig()>>config
-                getCidStore()>>store
             }
         def hash = HashCode.fromInt(123456789)
         def taskConfig = Mock(TaskConfig){
@@ -238,7 +233,7 @@ class CidObserverTest extends Specification {
             getConfig() >> taskConfig
         }
         store.open(DataConfig.create(session))
-        def observer = new CidObserver(session)
+        def observer = new CidObserver(session, store)
         observer.getTaskRelative(task, PATH)
         then:
             def e = thrown(Exception)
@@ -252,15 +247,14 @@ class CidObserverTest extends Specification {
 
     def 'should relativise workflow output dirs' (){
         when:
-            def config = [workflow:[data:[store:[location:'cid']]]]
+            def config = [workflow:[data:[enabled: true, store:[location:'cid']]]]
             def store = new DefaultCidStore();
             def session = Mock(Session) {
                 getOutputDir()>>OUTPUT_DIR
                 getConfig()>>config
-                getCidStore()>>store
             }
             store.open(DataConfig.create(session))
-            def observer = new CidObserver(session)
+            def observer = new CidObserver(session, store)
         then:
             observer.getWorkflowRelative(PATH) == EXPECTED
         where:
@@ -275,14 +269,13 @@ class CidObserverTest extends Specification {
 
     def 'should return exception when relativise workflow output dirs' (){
         when:
-            def config = [workflow:[data:[store:[location:'cid']]]]
+            def config = [workflow:[data:[enabled: true, store:[location:'cid']]]]
             def store = new DefaultCidStore();
             def session = Mock(Session) {
                 getOutputDir()>>OUTPUT_DIR
                 getConfig()>>config
-                getCidStore()>>store
             }
-            def observer = new CidObserver(session)
+            def observer = new CidObserver(session, store)
             observer.getWorkflowRelative(PATH)
         then:
             def e = thrown(Exception)
@@ -298,7 +291,7 @@ class CidObserverTest extends Specification {
     def 'should save workflow output' (){
         given:
         def folder = Files.createTempDirectory('test')
-        def config = [workflow:[data:[store:[location:folder.toString()]]]]
+        def config = [workflow:[data:[enabled: true, store:[location:folder.toString()]]]]
         def store = new DefaultCidStore();
         def outputDir = folder.resolve('results')
         def uniqueId = UUID.randomUUID()
@@ -314,7 +307,6 @@ class CidObserverTest extends Specification {
         }
         def session = Mock(Session) {
             getConfig()>>config
-            getCidStore()>>store
             getOutputDir()>>outputDir
             getWorkDir() >> workDir
             getWorkflowMetadata()>>metadata
@@ -323,7 +315,7 @@ class CidObserverTest extends Specification {
             getParams() >> new ScriptBinding.ParamsMap()
         }
         store.open(DataConfig.create(session))
-        def observer = new CidObserver(session)
+        def observer = new CidObserver(session, store)
 
         when: 'Starting workflow'
             observer.onFlowCreate(session)
diff --git a/modules/nextflow/src/test/groovy/nextflow/data/config/DataConfigTest.groovy b/modules/nextflow/src/test/groovy/nextflow/data/config/DataConfigTest.groovy
index 3c7ac18489..c604c5b86d 100644
--- a/modules/nextflow/src/test/groovy/nextflow/data/config/DataConfigTest.groovy
+++ b/modules/nextflow/src/test/groovy/nextflow/data/config/DataConfigTest.groovy
@@ -31,12 +31,22 @@ class DataConfigTest extends Specification {
         def config = new DataConfig(Map.of())
         then:
         config.store.location == Path.of('.').resolve('data').toAbsolutePath().normalize()
+        !config.enabled
+    }
+
+    def 'should create default with enable' () {
+        when:
+        def config = new DataConfig([enabled: true])
+        then:
+        config.store.location == Path.of('.').resolve('data').toAbsolutePath().normalize()
+        config.enabled
     }
 
     def 'should create data config' () {
         when:
-        def config = new DataConfig(store: [location: "/some/data/store"])
+        def config = new DataConfig(enabled: true, store: [location: "/some/data/store"])
         then:
         config.store.location == Path.of("/some/data/store")
+        config.enabled
     }
 }


Reviews:
user: login: pditommaso
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-01T16:30:19Z
user: login: jorgee
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T10:50:38Z
user: login: jorgee
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T10:51:58Z
user: login: pditommaso
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T10:58:12Z
user: login: jorgee
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T11:17:11Z
user: login: pditommaso
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T13:57:23Z
user: login: pditommaso
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T17:48:00Z
user: login: jorgee
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T18:12:39Z
user: login: jorgee
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T18:14:02Z

Review comments:
diff_hunk: @@ -31,8 +31,11 @@ class DataConfig {
 
     final DataStoreOpts store
 
+    final boolean enabled
+
     DataConfig(Map opts) {
         this.store = new DataStoreOpts(opts.store as Map ?: Map.of())
+        this.enabled = opts.store != null
path: modules/nextflow/src/main/groovy/nextflow/data/config/DataConfig.groovy
user: login: pditommaso
body: We may decided to an explicit logic to enable/disable it. Just a temporary flag 
created_at: 2025-03-01T16:30:19Z
author_association: MEMBER
diff_hunk: @@ -43,4 +48,18 @@ abstract class CidStoreFactory implements ExtensionPoint {
         return factory.newInstance(config)
     }
 
+    static CidStore getOrCreate(Session session) {
+        if( instance || initialized )
+            return instance
+        synchronized (session) {
+            if( instance || initialized )
+                return instance
+            initialized = true
+            final config = DataConfig.create(session)
+            if( !config.enabled )
+                return null
+            return instance = create(config)
+        }
+    }
path: modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
user: login: jorgee
body: Why synchronized per session and not static synchronized? and also is there any reason why initialized is set to true before checking if it is enabled?  
```suggestion
   synchronized static CidStore getOrCreate(Session session) {
        if( instance || initialized )
            return instance
        final config = DataConfig.create(session)
        if( !config.enabled )
            return null
        initialized = true
        return instance = create(config)
    }
```
created_at: 2025-03-03T10:50:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,6 +33,10 @@ import org.pf4j.ExtensionPoint
 @CompileStatic
 abstract class CidStoreFactory implements ExtensionPoint {
 
+    private static CidStore instance
+
+    private static boolean initialized
+
     protected abstract CidStore newInstance(DataConfig config)
 
     static CidStore create(DataConfig config){
path: modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
user: login: jorgee
body: ```suggestion
    private static CidStore create(DataConfig config){
```
created_at: 2025-03-03T10:51:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,4 +48,18 @@ abstract class CidStoreFactory implements ExtensionPoint {
         return factory.newInstance(config)
     }
 
+    static CidStore getOrCreate(Session session) {
+        if( instance || initialized )
+            return instance
+        synchronized (session) {
+            if( instance || initialized )
+                return instance
+            initialized = true
+            final config = DataConfig.create(session)
+            if( !config.enabled )
+                return null
+            return instance = create(config)
+        }
+    }
path: modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
user: login: pditommaso
body: That's the "double check locking with singleton", in a nutshell to avoid locking if the instance is already created.  https://www.baeldung.com/java-singleton-double-checked-locking

> why initialized is set to true 

not getting this
created_at: 2025-03-03T10:58:11Z
author_association: MEMBER
diff_hunk: @@ -43,4 +48,18 @@ abstract class CidStoreFactory implements ExtensionPoint {
         return factory.newInstance(config)
     }
 
+    static CidStore getOrCreate(Session session) {
+        if( instance || initialized )
+            return instance
+        synchronized (session) {
+            if( instance || initialized )
+                return instance
+            initialized = true
+            final config = DataConfig.create(session)
+            if( !config.enabled )
+                return null
+            return instance = create(config)
+        }
+    }
path: modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
user: login: jorgee
body: Ok, about the double check. But is it not better to lock per class and not per session argument? It is assuming that session object is always the same. Not sure if there is a case that it is not true.

Forget the initialized comment, I've got it. It is also to avoid unnecessary creates
created_at: 2025-03-03T11:17:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,4 +48,18 @@ abstract class CidStoreFactory implements ExtensionPoint {
         return factory.newInstance(config)
     }
 
+    static CidStore getOrCreate(Session session) {
+        if( instance || initialized )
+            return instance
+        synchronized (session) {
+            if( instance || initialized )
+                return instance
+            initialized = true
+            final config = DataConfig.create(session)
+            if( !config.enabled )
+                return null
+            return instance = create(config)
+        }
+    }
path: modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
user: login: pditommaso
body: Not a strong opinion on this. In practical terms nextflow uses exactly one session, so does not matter. For testing purposes it may be slightly better. Feel free to update otherwise. 
created_at: 2025-03-03T13:57:23Z
author_association: MEMBER
diff_hunk: @@ -62,4 +62,11 @@ abstract class CidStoreFactory implements ExtensionPoint {
         }
     }
 
+    static void clean(){
path: modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
user: login: pditommaso
body: Let's mark it with `@TestOnly`, also `reset` may be a slightly better for consistency with other similar methods eg 

https://github.com/nextflow-io/nextflow/blob/138a1ff7e73089f516c44a11f7e72cf3a384ede7/modules/nextflow/src/main/groovy/nextflow/secret/SecretsLoader.groovy#L62-L64
created_at: 2025-03-03T17:47:59Z
author_association: MEMBER
diff_hunk: @@ -62,4 +62,11 @@ abstract class CidStoreFactory implements ExtensionPoint {
         }
     }
 
+    static void clean(){
path: modules/nextflow/src/main/groovy/nextflow/data/cid/CidStoreFactory.groovy
user: login: jorgee
body:  Changed
created_at: 2025-03-03T18:12:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,8 +31,11 @@ class DataConfig {
 
     final DataStoreOpts store
 
+    final boolean enabled
+
     DataConfig(Map opts) {
         this.store = new DataStoreOpts(opts.store as Map ?: Map.of())
+        this.enabled = opts.store != null
path: modules/nextflow/src/main/groovy/nextflow/data/config/DataConfig.groovy
user: login: jorgee
body: I have added the workflow.data.enabled flag to enable/disable it
created_at: 2025-03-03T18:14:01Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-03-01T16:29:18Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *nextflow-docs-staging* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 700e5644d6dcb301568334a811adf845d1257287 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/nextflow-docs-staging/deploys/67c335cf5b05b10008934141 |
author_association: NONE
user: login: pditommaso
created_at: 2025-03-03T13:57:51Z
body: @jorgee can you help solving the broken tests? 🙏 
author_association: MEMBER
user: login: jorgee
created_at: 2025-03-03T14:04:25Z
body: > @jorgee can you help solving the broken tests? 🙏

Yes, I am on it.
author_association: CONTRIBUTOR
user: login: jorgee
created_at: 2025-03-03T18:26:55Z
body: I have solved broken tests, added the `workflow.data.enabled` flag and I have also managed the case where store is null in the `cid` command. I can be merged to cid-store from my side.
author_association: CONTRIBUTOR
user: login: pditommaso
created_at: 2025-03-03T18:54:12Z
body: Nice! 
author_association: MEMBER