add nextflow d30e48d
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2013-2026, Seqera Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package nextflow
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.util.logging.Slf4j
|
||||
import nextflow.plugin.BasePlugin
|
||||
import org.pf4j.PluginWrapper
|
||||
/**
|
||||
* Nextflow cloud cache plugin
|
||||
*
|
||||
* @author Ben Sherman <bentshermann@gmail.com>
|
||||
*/
|
||||
@Slf4j
|
||||
@CompileStatic
|
||||
class CloudCachePlugin extends BasePlugin {
|
||||
|
||||
CloudCachePlugin(PluginWrapper wrapper) {
|
||||
super(wrapper)
|
||||
}
|
||||
|
||||
}
|
||||
57
nextflow/plugins/nf-cloudcache/src/main/nextflow/cache/CloudCacheConfig.groovy
vendored
Normal file
57
nextflow/plugins/nf-cloudcache/src/main/nextflow/cache/CloudCacheConfig.groovy
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2013-2026, Seqera Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package nextflow.cache
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import nextflow.config.spec.ConfigOption
|
||||
import nextflow.config.spec.ConfigScope
|
||||
import nextflow.config.spec.ScopeName
|
||||
import nextflow.script.dsl.Description
|
||||
|
||||
/**
|
||||
* Model cloud cache configuration
|
||||
*
|
||||
* @author Paolo Di Tommaso <paolo.ditommaso@gmail.com>
|
||||
*/
|
||||
@ScopeName("cloudcache")
|
||||
@Description("""
|
||||
The `cloudcache` scope controls the use of object storage as cache storage for workflow execution metadata.
|
||||
""")
|
||||
@CompileStatic
|
||||
class CloudCacheConfig implements ConfigScope {
|
||||
|
||||
@ConfigOption
|
||||
@Description("""
|
||||
Enable the use of cloud cache (default: `false`).
|
||||
""")
|
||||
final boolean enabled
|
||||
|
||||
@ConfigOption
|
||||
@Description("""
|
||||
The path to the cloud storage bucket for cache metadata (e.g. `s3://bucket/cache`).
|
||||
""")
|
||||
final String path
|
||||
|
||||
/* required by extension point -- do not remove */
|
||||
CloudCacheConfig() {}
|
||||
|
||||
CloudCacheConfig(Map opts) {
|
||||
this.enabled = opts.enabled as boolean
|
||||
this.path = opts.path as String
|
||||
}
|
||||
|
||||
}
|
||||
48
nextflow/plugins/nf-cloudcache/src/main/nextflow/cache/CloudCacheFactory.groovy
vendored
Normal file
48
nextflow/plugins/nf-cloudcache/src/main/nextflow/cache/CloudCacheFactory.groovy
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2013-2026, Seqera Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package nextflow.cache
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import nextflow.Global
|
||||
import nextflow.Session
|
||||
import nextflow.exception.AbortOperationException
|
||||
import nextflow.plugin.Priority
|
||||
/**
|
||||
* Implements the cloud cache factory
|
||||
*
|
||||
* @see CloudCacheStore
|
||||
*
|
||||
* @author Ben Sherman <bentshermann@gmail.com>
|
||||
*/
|
||||
@CompileStatic
|
||||
@Priority(-10)
|
||||
class CloudCacheFactory extends CacheFactory {
|
||||
|
||||
@Override
|
||||
protected CacheDB newInstance(UUID uniqueId, String runName, Path home) {
|
||||
if( !uniqueId ) throw new AbortOperationException("Missing cache `uuid`")
|
||||
if( !runName ) throw new AbortOperationException("Missing cache `runName`")
|
||||
final path = (Global.session as Session).cloudCachePath
|
||||
if( !path )
|
||||
throw new IllegalArgumentException("Cloud-cache path not defined - use either -with-cloudcache run option or NXF_CLOUDCACHE_PATH environment variable")
|
||||
final store = new CloudCacheStore(uniqueId, runName, path)
|
||||
return new CacheDB(store)
|
||||
}
|
||||
|
||||
}
|
||||
160
nextflow/plugins/nf-cloudcache/src/main/nextflow/cache/CloudCacheStore.groovy
vendored
Normal file
160
nextflow/plugins/nf-cloudcache/src/main/nextflow/cache/CloudCacheStore.groovy
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2013-2026, Seqera Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package nextflow.cache
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.NoSuchFileException
|
||||
import java.nio.file.Path
|
||||
|
||||
import com.google.common.hash.HashCode
|
||||
import groovy.transform.CompileStatic
|
||||
import nextflow.exception.AbortOperationException
|
||||
import nextflow.extension.FilesEx
|
||||
import nextflow.util.CacheHelper
|
||||
/**
|
||||
* Implements the cloud cache store
|
||||
*
|
||||
* @author Ben Sherman <bentshermann@gmail.com>
|
||||
*/
|
||||
@CompileStatic
|
||||
class CloudCacheStore implements CacheStore {
|
||||
|
||||
private final int KEY_SIZE
|
||||
|
||||
/** The session UUID */
|
||||
private UUID uniqueId
|
||||
|
||||
/** The unique run name associated with this cache instance */
|
||||
private String runName
|
||||
|
||||
/** The base path for the entire cache */
|
||||
private Path basePath
|
||||
|
||||
/** The base path for this cache instance */
|
||||
private Path dataPath
|
||||
|
||||
/** The path to the index file */
|
||||
private Path indexPath
|
||||
|
||||
/** Index file input stream */
|
||||
private InputStream indexReader
|
||||
|
||||
/** Index file output stream */
|
||||
private OutputStream indexWriter
|
||||
|
||||
CloudCacheStore(UUID uniqueId, String runName, Path basePath) {
|
||||
assert uniqueId, "Missing cloudcache 'uniqueId' argument"
|
||||
assert runName, "Missing cloudcache 'runName' argument"
|
||||
assert basePath, "Missing cloudcache 'basePath' argument"
|
||||
this.KEY_SIZE = CacheHelper.hasher('x').hash().asBytes().size()
|
||||
this.uniqueId = uniqueId
|
||||
this.runName = runName
|
||||
this.basePath = basePath
|
||||
this.dataPath = this.basePath.resolve("$uniqueId")
|
||||
this.indexPath = dataPath.resolve("index.$runName")
|
||||
}
|
||||
|
||||
@Override
|
||||
CloudCacheStore open() {
|
||||
indexWriter = new BufferedOutputStream(Files.newOutputStream(indexPath))
|
||||
return this
|
||||
}
|
||||
|
||||
@Override
|
||||
CloudCacheStore openForRead() {
|
||||
if( !dataPath.exists() )
|
||||
throw new AbortOperationException("Missing cache directory: $dataPath")
|
||||
indexReader = Files.newInputStream(indexPath)
|
||||
return this
|
||||
}
|
||||
|
||||
@Override
|
||||
void drop() {
|
||||
dataPath.deleteDir()
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() {
|
||||
FilesEx.closeQuietly(indexWriter)
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeIndex(HashCode key, boolean cached) {
|
||||
indexWriter.write(key.asBytes())
|
||||
indexWriter.write(cached ? 1 : 0)
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteIndex() {
|
||||
indexPath.delete()
|
||||
}
|
||||
|
||||
@Override
|
||||
Iterator<Index> iterateIndex() {
|
||||
return new Iterator<Index>() {
|
||||
private Index next
|
||||
|
||||
{
|
||||
next = fetch()
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasNext() {
|
||||
return next != null
|
||||
}
|
||||
|
||||
@Override
|
||||
Index next() {
|
||||
final result = next
|
||||
next = fetch()
|
||||
return result
|
||||
}
|
||||
|
||||
private Index fetch() {
|
||||
byte[] key = new byte[KEY_SIZE]
|
||||
if( indexReader.read(key) == -1 )
|
||||
return null
|
||||
final cached = indexReader.read() == 1
|
||||
return new Index(HashCode.fromBytes(key), cached)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] getEntry(HashCode key) {
|
||||
try {
|
||||
return getCachePath(key).bytes
|
||||
}
|
||||
catch( NoSuchFileException e ) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void putEntry(HashCode key, byte[] value) {
|
||||
getCachePath(key).bytes = value
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteEntry(HashCode key) {
|
||||
getCachePath(key).delete()
|
||||
}
|
||||
|
||||
private Path getCachePath(HashCode key) {
|
||||
dataPath.resolve(key.toString())
|
||||
}
|
||||
}
|
||||
43
nextflow/plugins/nf-cloudcache/src/test/nextflow/cache/CloudCacheConfigTest.groovy
vendored
Normal file
43
nextflow/plugins/nf-cloudcache/src/test/nextflow/cache/CloudCacheConfigTest.groovy
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2013-2026, Seqera Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package nextflow.cache
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paolo Di Tommaso <paolo.ditommaso@gmail.com>
|
||||
*/
|
||||
class CloudCacheConfigTest extends Specification {
|
||||
|
||||
def 'should create empty config' () {
|
||||
when:
|
||||
def config = new CloudCacheConfig([:])
|
||||
then:
|
||||
!config.enabled
|
||||
config.path == null
|
||||
}
|
||||
|
||||
def 'should create config with all options' () {
|
||||
when:
|
||||
def config = new CloudCacheConfig([enabled: true, path: 's3://bucket/cache'])
|
||||
then:
|
||||
config.enabled
|
||||
config.path == 's3://bucket/cache'
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user