Repository: bacalhau-project/bacalhau (language: python)

Issue:
Delete the inmemory job store
This is no longer required as we the boltdb store performs well enough, and as we add another jobstore the inmemory one is just more unused code to maintain.


Discussion / hints:

Fix patch:
diff --git a/cmd/util/flags/configflags/compute_storage.go b/cmd/util/flags/configflags/compute_storage.go
index e52ffebc3a..464e02cb90 100644
--- a/cmd/util/flags/configflags/compute_storage.go
+++ b/cmd/util/flags/configflags/compute_storage.go
@@ -7,7 +7,7 @@ var ComputeStorageFlags = []Definition{
 		FlagName:             "compute-execution-store-type",
 		ConfigPath:           types.NodeComputeExecutionStoreType,
 		DefaultValue:         Default.Node.Compute.ExecutionStore.Type,
-		Description:          "The type of store used by the compute node (BoltDB or InMemory)",
+		Description:          "The type of store used by the compute node (BoltDB)",
 		EnvironmentVariables: []string{"BACALHAU_COMPUTE_STORE_TYPE"},
 	},
 	{
diff --git a/cmd/util/flags/configflags/job_storage.go b/cmd/util/flags/configflags/job_storage.go
index 7a9cc7d9fc..40980fc228 100644
--- a/cmd/util/flags/configflags/job_storage.go
+++ b/cmd/util/flags/configflags/job_storage.go
@@ -7,7 +7,7 @@ var RequesterJobStorageFlags = []Definition{
 		FlagName:             "requester-job-store-type",
 		ConfigPath:           types.NodeRequesterJobStoreType,
 		DefaultValue:         Default.Node.Requester.JobStore.Type,
-		Description:          "The type of job store used by the requester node (BoltDB or InMemory)",
+		Description:          "The type of job store used by the requester node (BoltDB)",
 		EnvironmentVariables: []string{"BACALHAU_JOB_STORE_TYPE"},
 	},
 	{
diff --git a/docs/docs/dev/cli-reference/all-flags.md b/docs/docs/dev/cli-reference/all-flags.md
index 3c43664b7e..236a3b39d2 100644
--- a/docs/docs/dev/cli-reference/all-flags.md
+++ b/docs/docs/dev/cli-reference/all-flags.md
@@ -60,28 +60,28 @@ Global Flags
 <details>
   <summary>`--api-host string`</summary>
   <div>
-    <div>Determines the host for RESTful communication between the client and server. This flag is ignored if the `BACALHAU_API_HOST` environment variable is set.  
+    <div>Determines the host for RESTful communication between the client and server. This flag is ignored if the `BACALHAU_API_HOST` environment variable is set.
          Default: `bootstrap.production.bacalhau.org`</div>
   </div>
 </details>
 <details>
   <summary>`--api-port int`</summary>
   <div>
-    <div>Determines the port for RESTful communication between the client and server. This flag is ignored if the `BACALHAU_API_PORT` environment variable is active.  
+    <div>Determines the port for RESTful communication between the client and server. This flag is ignored if the `BACALHAU_API_PORT` environment variable is active.
          Default: `1234` </div>
   </div>
 </details>
 <details>
   <summary>`--log-mode logging-mode`</summary>
   <div>
-    <div>Determines the preferred log format. Available log formats are: `default`, `station`, `json`, `combined`, `event`.  
+    <div>Determines the preferred log format. Available log formats are: `default`, `station`, `json`, `combined`, `event`.
          Default: `default`</div>
   </div>
 </details>
 <details>
   <summary>`--repo string`</summary>
   <div>
-    <div>Specifies the path to the bacalhau repository.  
+    <div>Specifies the path to the bacalhau repository.
          Default: `$HOME/.bacalhau`</div>
   </div>
 </details>
@@ -239,7 +239,7 @@ Expected Output:
 }
 ```
 
-## Cancel 
+## Cancel
 
 The `bacalhau cancel` command cancels a job that was previously submitted and stops it running if it has not yet completed.
 
@@ -255,7 +255,7 @@ Flags:
       --quiet   Do not print anything to stdout or stderr
 ```
 
-#### Examples 
+#### Examples
 
 To cancel a previously submitted job, run:
 
@@ -312,10 +312,10 @@ Flags:
       --no-descriptions   disable completion descriptions
 ```
 :::info
-To load completions in your current shell session:  
-	`bacalhau completion fish | source`  
-To load completions for every new session, execute once:  
-	`bacalhau completion fish > ~/.config/fish/completions/bacalhau.fish`  
+To load completions in your current shell session:
+	`bacalhau completion fish | source`
+To load completions for every new session, execute once:
+	`bacalhau completion fish > ~/.config/fish/completions/bacalhau.fish`
 You will need to start a new shell for this setup to take effect.
 :::
 
@@ -334,8 +334,8 @@ Flags:
 ```
 
 :::info
-To load completions in your current shell session:  
-	`bacalhau completion powershell | Out-String | Invoke-Expression`  
+To load completions in your current shell session:
+	`bacalhau completion powershell | Out-String | Invoke-Expression`
 To load completions for every new session, add the output of the above command
 to your powershell profile.
 :::
@@ -355,7 +355,7 @@ Flags:
 
 :::info
 If shell completion is not already enabled in your environment you will need
-to enable it.  You can execute the following once:  
+to enable it.  You can execute the following once:
 	`echo "autoload -U compinit; compinit" >> ~/.zshrc`
 :::
 
@@ -525,7 +525,7 @@ KEY                                                             VALUE
                                                                  VE3wQNSSkxmYoaBPHVAWdjBjDYmMkWvtMZxAf]
  node.clientapi.host                                             bootstrap.production.bacalhau.org
  node.clientapi.port                                             1234
- 
+
  ...
 ```
 
@@ -554,7 +554,7 @@ Verifying that the parameter was successfully set
 
 ```shell
 bacalhau config list | grep serverapi.port
- node.serverapi.port                                             9999                                     
+ node.serverapi.port                                             9999
 cat ~/.bacalhau/config.yaml
 node:
     serverapi:
@@ -713,7 +713,7 @@ Using a UCAN Invocation object allows you to customize the parameters of job exe
 
 ## Describe
 
-The `bacalhau describe` command provides a full description of a job in YAML format. Short form and long form of the job id are accepted.  
+The `bacalhau describe` command provides a full description of a job in YAML format. Short form and long form of the job id are accepted.
 
 Usage:
 
@@ -744,7 +744,7 @@ bacalhau describe e3f8c209
 3. To describe a job and include all server and local events, run:
 
 ```shell
-bacalhau describe e3f8c209 --include-events  
+bacalhau describe e3f8c209 --include-events
 ```
 
 ## Devstack
@@ -884,7 +884,7 @@ bacalhau docker run \
   dpokidov/imagemagick:7.1.0-47-ubuntu \
   -- magick mogrify -resize 100x100 -quality 100 -path /outputs '/input_images/*.jpg'
 ```
-This command allows you to start a job in a Docker container using the specified image, mount an external CID resource from IPFS inside the container to handle images, and execute a command inside the container to process files. 
+This command allows you to start a job in a Docker container using the specified image, mount an external CID resource from IPFS inside the container to handle images, and execute a command inside the container to process files.
 
 2. To check the job specification before submitting it to the bacalhau network, run:
 
@@ -1002,7 +1002,7 @@ bacalhau get 51225160
 
 ## Help
 
-The `bacalhau help` command provides help for any command in the application. 
+The `bacalhau help` command provides help for any command in the application.
 
 Usage:
 
@@ -1047,7 +1047,7 @@ Usage:
 
 Available Commands:
 
-### describe 
+### describe
 
 ```shell
 bacalhau job describe [id] [flags]
@@ -1113,7 +1113,7 @@ CREATED   MODIFIED  ID          NODE ID   REV.  COMPUTE    DESIRED  COMMENT
 16:46:03  16:46:04  e-03870df5  QmVXwmdZ  6     Completed  Stopped
 ```
 
-2. To get executions with YAML output, run: 
+2. To get executions with YAML output, run:
 
 ```shell
 bacalhau job executions j-4faae6f0-17b3-4a6d-991e-c82a677c7228 --output yaml
@@ -1394,7 +1394,7 @@ This configuration describes a batch job that runs a Docker task. It utilizes th
 
  ```bash
  bacalhau job run job.yaml
- 
+
  Expected Output:
  Job successfully submitted. Job ID: j-2d0f513a-9eb1-49c2-8bc8-246c6fb41520
    Checking job status... (Enter Ctrl+C to exit at any time, your job will continue running):
@@ -1461,7 +1461,7 @@ Checking job status... (Enter Ctrl+C to exit at any time, your job will continue
 Job Results By Node:
 • Node QmSD38wH:
 	Hello Bacalhau!
-	
+
 To get more details about the run, execute:
 	bacalhau job describe j-3634acc2-c92c-494d-9413-ddd8629d0e74
 To get more details about the run executions, execute:
@@ -1562,7 +1562,7 @@ bacalhau list
 bacalhau list --output json
 ```
 
-## Logs 
+## Logs
 
 The `bacalhau logs` command retrieves the log output (stdout, and stderr) from a job.
 If the job is still running it is possible to follow the logs after the previously generated logs are retrieved.
@@ -1578,7 +1578,7 @@ Flags:
   -h, --help     help for logs
 ```
 
-#### Examples 
+#### Examples
 
 1. To follow logs for a previously submitted job, run:
 
@@ -1603,7 +1603,7 @@ bacalhau node [command]
 
 Available Commands:
 
-### describe 
+### describe
 
 ```shell
 bacalhau node describe [id] [flags]
@@ -1617,7 +1617,7 @@ Flags:
       --pretty          Pretty print the output. Only applies to json and yaml output formats.
 ```
 
-#### Examples 
+#### Examples
 
 1. To Describe a Node with the `QmSD38wH` ID, run:
 
@@ -1674,7 +1674,7 @@ Expected Output:
 ### list
 
 ```shell
- bacalhau node list [flags] 
+ bacalhau node list [flags]
 ```
 The `bacalhau node list` command is designed to provide users with a comprehensive list of network nodes along with details based on specified flags.  It supports various filtering, ordering, and output formatting options, allowing users to tailor the output to their needs.
 
@@ -1694,7 +1694,7 @@ Flags:
       --wide                Print full values in the table results without truncation.
 ```
 
-#### Examples 
+#### Examples
 
 1. To retrieve the list of nodes, run:
 
@@ -1776,7 +1776,7 @@ Expected Output:
       "git-lfs": "True",
       "owner": "bacalhau"
     },
-   ... 
+   ...
 ```
 
 ## Serve
@@ -1794,7 +1794,7 @@ Flags:
       --autocert string                                  Specifies a host name for which ACME is used to obtain a TLS Certificate.
                                                          Using this option results in the API serving over HTTPS
       --compute-execution-store-path string              The path used for the compute execution store when using BoltDB
-      --compute-execution-store-type storage-type        The type of store used by the compute node (BoltDB or InMemory) (default BoltDB)
+      --compute-execution-store-type storage-type        The type of store used by the compute node (BoltDB) (default BoltDB)
       --default-job-execution-timeout duration           default value for the execution timeout this compute node will assign to jobs with no timeout requirement defined. (default 10m0s)
       --disable-engine strings                           Engine types to disable
       --disable-storage strings                          Storage types to disable
@@ -1827,14 +1827,14 @@ Flags:
       --port int                                         The port to server on. (default 1234)
       --private-internal-ipfs                            Whether the in-process IPFS node should auto-discover other nodes, including the public IPFS network - cannot be used with --ipfs-connect. Use "--private-internal-ipfs=false" to disable. To persist a local Ipfs node, set BACALHAU_SERVE_IPFS_PATH to a valid path. (default true)
       --requester-job-store-path string                  The path used for the requester job store store when using BoltDB
-      --requester-job-store-type storage-type            The type of job store used by the requester node (BoltDB or InMemory) (default BoltDB)
+      --requester-job-store-type storage-type            The type of job store used by the requester node (BoltDB) (default BoltDB)
       --swarm-port int                                   The port to listen on for swarm connections. (default 1235)
       --tlscert string                                   Specifies a TLS certificate file to be used by the requester node
       --tlskey string                                    Specifies a TLS key file matching the certificate to be used by the requester node
       --web-ui                                           Whether to start the web UI alongside the bacalhau node.
 ```
 
-#### Examples 
+#### Examples
 
 1. To Start a private bacalhau requester node, you can run either of these two commands:
 
@@ -1890,7 +1890,7 @@ Flags:
       --output-schema   Output the JSON schema for a Job to stdout then exit
 ```
 
-#### Example 
+#### Example
 
 To Validate the `job.yaml` file, run:
 
@@ -1998,7 +1998,7 @@ bacalhau wasm run <localfile.wasm>
 bacalhau wasm run <cid>
 ```
 
-### validate 
+### validate
 
 ```shell
 bacalhau wasm validate <local.wasm> [--entry-point <string>] [flags]
@@ -2010,4 +2010,4 @@ The `bacalhau wasm validate` command Checks that a WASM program is runnable on t
 Flags:
       --entry-point string   The name of the WASM function in the entry module to call. This should be a zero-parameter zero-result function that will execute the job. (default "_start")
   -h, --help                 help for validate
-```
\ No newline at end of file
+```
diff --git a/docs/docs/dev/databases.md b/docs/docs/dev/databases.md
index 3a8f230e25..95f5b1b24a 100644
--- a/docs/docs/dev/databases.md
+++ b/docs/docs/dev/databases.md
@@ -2,7 +2,7 @@
 
 ## Requester node database (job store)
 
-Requester nodes store job state and history in an in-memory store (pkg/jobstore/inmemory), but can be configured to persist this information in a boltdb database on disk by setting the `BACALHAU_JOB_STORE_TYPE` environment variable to `boltdb`. To explicitly set to inmemory, the value should be `inmemory`.
+Requester nodes store job state and history in a boltdb-backed store (pkg/jobstore/boltdb).
 
 The location of the database file can be specified using the `BACALHAU_JOB_STORE_PATH` environment variable, which will specify which file to use to store the database.  When not specified, the file will be `{$BACALHAU_DIR}/{NODE_ID}-requester.db`.
 
diff --git a/docs/docs/setting-up/running-node/persistence.md b/docs/docs/setting-up/running-node/persistence.md
index 047f68ce0a..3d836589f7 100644
--- a/docs/docs/setting-up/running-node/persistence.md
+++ b/docs/docs/setting-up/running-node/persistence.md
@@ -5,14 +5,14 @@ title: 'Configuring node persistence'
 description: How to configure compute/requester persistence
 ---
 
-Both compute nodes, and requester nodes, maintain state. How that state is maintained is configurable, although the defaults are likely adequate for most use-cases.  This page describes how to configure the persistence of compute and requester nodes should the defaults not be suitable.  
+Both compute nodes, and requester nodes, maintain state. How that state is maintained is configurable, although the defaults are likely adequate for most use-cases.  This page describes how to configure the persistence of compute and requester nodes should the defaults not be suitable.
 
-## Compute node persistence 
+## Compute node persistence
 
 The computes nodes maintain information about the work that has been allocated to them, including:
 
 * The current state of the execution, and
-* The original job that resulted in this allocation 
+* The original job that resulted in this allocation
 
 This information is used by the compute and requester nodes to ensure allocated jobs are completed successfully.  By default, compute nodes store their state in a bolt-db database and this is located in the bacalhau repository along with configuration data.  For a compute node whose ID is "abc", the database can be found in `~/.bacalhau/abc-compute/executions.db`.
 
@@ -28,10 +28,8 @@ In some cases, it may be preferable to maintain the state in memory, with the ca
 
 When running a requester node, it maintains state about the jobs it has been requested to orchestrate and schedule, the evaluation of those jobs, and the executions that have been allocated.  By default, this state is stored in a bolt db database that, with a node ID of "xyz" can be found in  `~/.bacalhau/xyz-requester/jobs.db`.
 
-It is possible, but not recommended, to run the requester node with an inmemory job store, which will result in lost state when the requester node is restarted.  
 
 |Environment Variable|Flag alternative|Value|Effect|
 |--|--|--|--|
 |BACALHAU_JOB_STORE_TYPE|--requester-job-store-type|boltdb|Uses the bolt db job store (default)|
 |BACALHAU_JOB_STORE_PATH|--requester-job-store-path|A path (inc. filename)|Specifies where the boltdb database should be stored. Default is `~/.bacalhau/{NODE-ID}-requester/jobs.db` if not set|
-|BACALHAU_JOB_STORE_TYPE|--requester-job-store-type|inmemory|Uses the inmemory job store|
diff --git a/pkg/jobstore/boltdb/store.go b/pkg/jobstore/boltdb/store.go
index 319774c6da..5177929cdc 100644
--- a/pkg/jobstore/boltdb/store.go
+++ b/pkg/jobstore/boltdb/store.go
@@ -1150,5 +1150,5 @@ func (b *BoltJobStore) Close(ctx context.Context) error {
 	return b.database.Close()
 }
 
-// Static check to ensure that InMemoryJobStore implements jobstore.Store
+// Static check to ensure that BoltJobStore implements jobstore.Store
 var _ jobstore.Store = (*BoltJobStore)(nil)
diff --git a/pkg/jobstore/inmemory/inmemory.go b/pkg/jobstore/inmemory/inmemory.go
deleted file mode 100644
index 504b8fab9f..0000000000
--- a/pkg/jobstore/inmemory/inmemory.go
+++ /dev/null
@@ -1,540 +0,0 @@
-package inmemory
-
-import (
-	"context"
-	"encoding/json"
-	"sort"
-	"strings"
-	"sync"
-	"time"
-
-	"github.com/bacalhau-project/bacalhau/pkg/util/idgen"
-	"github.com/benbjohnson/clock"
-	"github.com/imdario/mergo"
-	"golang.org/x/exp/maps"
-	"golang.org/x/exp/slices"
-	"k8s.io/apimachinery/pkg/labels"
-
-	"github.com/bacalhau-project/bacalhau/pkg/bacerrors"
-	"github.com/bacalhau-project/bacalhau/pkg/jobstore"
-	"github.com/bacalhau-project/bacalhau/pkg/models"
-)
-
-const newJobComment = "Job created"
-
-type InMemoryJobStore struct {
-	// jobs is a map of job ID to job
-	jobs map[string]models.Job
-	// executions is map of execution ID to execution
-	executions map[string]models.Execution
-	// jobExecutions is a map of job ID to list of execution IDs
-	jobExecutions map[string][]string
-	// history is a map of job ID to job history
-	history map[string][]models.JobHistory
-	// inProgress is a set of job IDs that are in progress
-	inProgress map[string]struct{}
-	// evaluations is a map of evaluation ID to evaluation
-	evaluations map[string]models.Evaluation
-	watchers    []jobstore.Watcher
-	watcherLock sync.Mutex
-	mtx         sync.RWMutex
-	clock       clock.Clock
-}
-
-type Option func(store *(InMemoryJobStore))
-
-func WithClock(clock clock.Clock) Option {
-	return func(store *InMemoryJobStore) {
-		store.clock = clock
-	}
-}
-
-func NewInMemoryJobStore(options ...Option) *InMemoryJobStore {
-	res := &InMemoryJobStore{
-		jobs:          make(map[string]models.Job),
-		executions:    make(map[string]models.Execution),
-		jobExecutions: make(map[string][]string),
-		history:       make(map[string][]models.JobHistory),
-		inProgress:    make(map[string]struct{}),
-		evaluations:   make(map[string]models.Evaluation),
-		watchers:      make([]jobstore.Watcher, 1),
-		clock:         clock.New(),
-	}
-	for _, opt := range options {
-		opt(res)
-	}
-
-	return res
-}
-
-func (d *InMemoryJobStore) Watch(c context.Context, t jobstore.StoreWatcherType, e jobstore.StoreEventType) chan jobstore.WatchEvent {
-	w := jobstore.NewWatcher(t, e)
-
-	d.watcherLock.Lock()
-	d.watchers = append(d.watchers, *w)
-	d.watcherLock.Unlock()
-
-	return w.Channel()
-}
-
-func (d *InMemoryJobStore) triggerEvent(t jobstore.StoreWatcherType, e jobstore.StoreEventType, object interface{}) {
-	data, _ := json.Marshal(object)
-
-	for _, w := range d.watchers {
-		if w.IsWatchingEvent(e) && w.IsWatchingType(t) {
-			w.Channel() <- jobstore.WatchEvent{
-				Kind:   t,
-				Event:  e,
-				Object: data,
-			}
-		}
-	}
-}
-
-// Gets a job from the datastore.
-//
-// Errors:
-//
-//   - error-job-not-found        		  -- if the job is not found
-func (d *InMemoryJobStore) GetJob(_ context.Context, id string) (models.Job, error) {
-	d.mtx.RLock()
-	defer d.mtx.RUnlock()
-	return d.getJob(id)
-}
-
-//nolint:gocyclo,funlen
-func (d *InMemoryJobStore) GetJobs(ctx context.Context, query jobstore.JobQuery) (*jobstore.JobQueryResponse, error) {
-	d.mtx.RLock()
-	defer d.mtx.RUnlock()
-	var result []models.Job
-
-	for _, j := range maps.Values(d.jobs) {
-		if !query.ReturnAll && query.Namespace != "" && query.Namespace != j.Namespace {
-			// Job is not for the requesting client, so ignore it.
-			continue
-		}
-
-		// If we are not using include tags, by default every job is included.
-		// If a job is specifically excluded, that overrides it being included.
-		included := len(query.IncludeTags) == 0
-		for tag := range j.Labels {
-			if slices.Contains(query.IncludeTags, tag) {
-				included = true
-			}
-			if slices.Contains(query.ExcludeTags, tag) {
-				included = false
-				break
-			}
-		}
-
-		if !included {
-			continue
-		}
-
-		result = append(result, j)
-	}
-
-	listSorter := func(i, j int) bool {
-		switch query.SortBy {
-		case "modified_at":
-			if query.SortReverse {
-				return result[i].ModifyTime > result[j].ModifyTime
-			} else {
-				return result[i].ModifyTime < result[j].ModifyTime
-			}
-		default:
-			// We apply createdat as a default sort so that we can use it for pagination.
-			// Without a known default we won't have a stable sort that makes sense for
-			// offsets/limits.
-			if query.SortReverse {
-				return result[i].CreateTime > result[j].CreateTime
-			} else {
-				return result[i].CreateTime < result[j].CreateTime
-			}
-		}
-	}
-	sort.Slice(result, listSorter)
-
-	if query.Selector != nil {
-		var filtered []models.Job
-		for _, j := range result {
-			if query.Selector.Matches(labels.Set(j.Labels)) {
-				filtered = append(filtered, j)
-			}
-		}
-		result = filtered
-	}
-
-	// To be able to extract based on offset and limit, we need the records to be sorted in
-	// the same order for each query.  This is why we sort the list above and why the offset
-	// and limit are applied so late.
-	if query.Offset > 0 && query.Offset <= uint32(len(result)) {
-		result = result[query.Offset:]
-	}
-
-	if query.Limit > 0 && query.Limit <= uint32(len(result)) {
-		result = result[:query.Limit+query.Offset]
-	}
-
-	response := &jobstore.JobQueryResponse{
-		Jobs:       result,
-		Offset:     query.Offset,
-		Limit:      query.Limit,
-		NextOffset: query.Limit + query.Offset,
-	}
-
-	return response, nil
-}
-
-func (d *InMemoryJobStore) GetExecutions(_ context.Context, jobID string) ([]models.Execution, error) {
-	d.mtx.RLock()
-	defer d.mtx.RUnlock()
-
-	jobID, err := d.reifyJobID(jobID)
-	if err != nil {
-		return nil, err
-	}
-
-	executionIDs, ok := d.jobExecutions[jobID]
-	if !ok {
-		return nil, bacerrors.NewJobNotFound(jobID)
-	}
-	result := make([]models.Execution, 0, len(executionIDs))
-	for _, id := range executionIDs {
-		result = append(result, d.executions[id])
-	}
-	return result, nil
-}
-
-func (d *InMemoryJobStore) GetInProgressJobs(ctx context.Context) ([]models.Job, error) {
-	d.mtx.RLock()
-	defer d.mtx.RUnlock()
-	var result []models.Job
-	for id := range d.inProgress {
-		result = append(result, d.jobs[id])
-	}
-	return result, nil
-}
-
-func (d *InMemoryJobStore) GetJobHistory(_ context.Context, jobID string,
-	options jobstore.JobHistoryFilterOptions) ([]models.JobHistory, error) {
-	d.mtx.RLock()
-	defer d.mtx.RUnlock()
-
-	jobID, err := d.reifyJobID(jobID)
-	if err != nil {
-		return nil, err
-	}
-
-	history, ok := d.history[jobID]
-	if !ok {
-		return nil, jobstore.NewErrJobNotFound(jobID)
-	}
-
-	// We want to filter events to only those that happened after the timestamp provided
-	sinceTime := options.Since
-	eventList := make([]models.JobHistory, 0, len(history))
-	for _, event := range history {
-		if options.ExcludeExecutionLevel && event.Type == models.JobHistoryTypeExecutionLevel {
-			continue
-		}
-
-		if options.ExcludeJobLevel && event.Type == models.JobHistoryTypeJobLevel {
-			continue
-		}
-
-		if options.ExecutionID != "" && strings.HasPrefix(event.ExecutionID, options.ExecutionID) {
-			continue
-		}
-
-		if options.NodeID != "" && strings.HasPrefix(event.NodeID, options.NodeID) {
-			continue
-		}
-
-		if event.Time.Unix() >= sinceTime {
-			eventList = append(eventList, event)
-		}
-	}
-
-	history = eventList
-	sort.Slice(history, func(i, j int) bool { return history[i].Time.UTC().Before(history[j].Time.UTC()) })
-
-	return history, nil
-}
-
-func (d *InMemoryJobStore) CreateJob(_ context.Context, job models.Job) error {
-	d.mtx.Lock()
-	defer d.mtx.Unlock()
-	existingJob, ok := d.jobs[job.ID]
-	if ok {
-		return jobstore.NewErrJobAlreadyExists(existingJob.ID)
-	}
-	job.State = models.NewJobState(models.JobStateTypePending)
-	job.Revision = 1
-	job.CreateTime = d.clock.Now().UTC().UnixNano()
-	job.ModifyTime = d.clock.Now().UTC().UnixNano()
-	job.Normalize()
-
-	err := job.Validate()
-	if err != nil {
-		return err
-	}
-	d.jobs[job.ID] = job
-
-	// populate job state
-	d.jobExecutions[job.ID] = []string{}
-	d.inProgress[job.ID] = struct{}{}
-	d.appendJobHistory(job, models.JobStateTypePending, newJobComment)
-
-	d.triggerEvent(jobstore.JobWatcher, jobstore.CreateEvent, job)
-
-	return nil
-}
-
-// DeleteJob removes a job from storage
-func (d *InMemoryJobStore) DeleteJob(ctx context.Context, jobID string) error {
-	job := d.jobs[jobID]
-
-	delete(d.jobs, jobID)
-	delete(d.jobExecutions, jobID)
-	delete(d.inProgress, jobID)
-	delete(d.history, jobID)
-
-	d.triggerEvent(jobstore.JobWatcher, jobstore.DeleteEvent, job)
-	return nil
-}
-
-// helper method to read a single job from memory. This is used by both GetJob and GetJobs.
-// It is important that we don't attempt to acquire a lock inside this method to avoid deadlocks since
-// the callers are expected to be holding a lock, and golang doesn't support reentrant locks.
-func (d *InMemoryJobStore) getJob(id string) (models.Job, error) {
-	id, err := d.reifyJobID(id)
-	if err != nil {
-		return models.Job{}, err
-	}
-
-	j, ok := d.jobs[id]
-	if !ok {
-		returnError := bacerrors.NewJobNotFound(id)
-		return models.Job{}, returnError
-	}
-
-	return j, nil
-}
-
-// reifyJobID ensures the provided job ID is a full-length ID. This is either through
-// returning the ID, or resolving the short ID to a single job id.
-func (d *InMemoryJobStore) reifyJobID(id string) (string, error) {
-	if idgen.ShortID(id) == id {
-		found := make([]string, 0, 1)
-		// passed in a short id, need to resolve the long id first
-		for k := range d.jobs {
-			if idgen.ShortID(k) == id {
-				found = append(found, k)
-			}
-		}
-		switch len(found) {
-		case 0:
-			return "", bacerrors.NewJobNotFound(id)
-		case 1:
-			return found[0], nil
-		default:
-			return "", bacerrors.NewMultipleJobsFound(id, found)
-		}
-	}
-	return id, nil
-}
-
-func (d *InMemoryJobStore) UpdateJobState(_ context.Context, request jobstore.UpdateJobStateRequest) error {
-	d.mtx.Lock()
-	defer d.mtx.Unlock()
-
-	// get the existing job state
-	job, ok := d.jobs[request.JobID]
-	if !ok {
-		return jobstore.NewErrJobNotFound(request.JobID)
-	}
-
-	// check the expected state
-	if err := request.Condition.Validate(job); err != nil {
-		return err
-	}
-	if job.IsTerminal() {
-		return jobstore.NewErrJobAlreadyTerminal(request.JobID, job.State.StateType, request.NewState)
-	}
-
-	// update the job state
-	previousState := job.State.StateType
-	job.State.StateType = request.NewState
-	job.Revision++
-	job.ModifyTime = d.clock.Now().UTC().UnixNano()
-	d.jobs[request.JobID] = job
-	if job.IsTerminal() {
-		delete(d.inProgress, request.JobID)
-	}
-	d.appendJobHistory(job, previousState, request.Comment)
-
-	d.triggerEvent(jobstore.JobWatcher, jobstore.UpdateEvent, job)
-
-	return nil
-}
-
-func (d *InMemoryJobStore) CreateExecution(_ context.Context, execution models.Execution) error {
-	d.mtx.Lock()
-	defer d.mtx.Unlock()
-	if _, ok := d.jobs[execution.JobID]; !ok {
-		return jobstore.NewErrJobNotFound(execution.JobID)
-	}
-	if _, ok := d.executions[execution.ID]; ok {
-		return jobstore.NewErrExecutionAlreadyExists(execution.ID)
-	}
-	if execution.CreateTime == 0 {
-		execution.CreateTime = d.clock.Now().UTC().UnixNano()
-	}
-	if execution.ModifyTime == 0 {
-		execution.ModifyTime = execution.CreateTime
-	}
-	if execution.Revision == 0 {
-		execution.Revision = 1
-	}
-	execution.Normalize()
-	d.executions[execution.ID] = execution
-	d.jobExecutions[execution.JobID] = append(d.jobExecutions[execution.JobID], execution.ID)
-	d.appendExecutionHistory(execution, models.ExecutionStateNew, "")
-
-	d.triggerEvent(jobstore.ExecutionWatcher, jobstore.CreateEvent, execution)
-
-	return nil
-}
-
-func (d *InMemoryJobStore) UpdateExecution(_ context.Context, request jobstore.UpdateExecutionRequest) error {
-	d.mtx.Lock()
-	defer d.mtx.Unlock()
-
-	// find the existing execution
-	existingExecution, ok := d.executions[request.ExecutionID]
-	if !ok {
-		return jobstore.NewErrExecutionNotFound(request.ExecutionID)
-	}
-
-	// check the expected state
-	if err := request.Condition.Validate(existingExecution); err != nil {
-		return err
-	}
-	if existingExecution.IsTerminalComputeState() {
-		return jobstore.NewErrExecutionAlreadyTerminal(
-			request.ExecutionID, existingExecution.ComputeState.StateType, request.NewValues.ComputeState.StateType)
-	}
-
-	// populate default values, maintain existing execution createTime
-	newExecution := request.NewValues
-	newExecution.CreateTime = existingExecution.CreateTime
-	if newExecution.ModifyTime == 0 {
-		newExecution.ModifyTime = d.clock.Now().UTC().UnixNano()
-	}
-	if newExecution.Revision == 0 {
-		newExecution.Revision = existingExecution.Revision + 1
-	}
-	newExecution.Normalize()
-
-	err := mergo.Merge(&newExecution, existingExecution)
-	if err != nil {
-		return err
-	}
-
-	// update the execution
-	previousState := existingExecution.ComputeState.StateType
-	d.executions[existingExecution.ID] = newExecution
-	d.appendExecutionHistory(newExecution, previousState, request.Comment)
-
-	d.triggerEvent(jobstore.ExecutionWatcher, jobstore.UpdateEvent, newExecution)
-
-	return nil
-}
-
-// CreateEvaluation creates a new evaluation
-func (d *InMemoryJobStore) CreateEvaluation(ctx context.Context, eval models.Evaluation) error {
-	d.mtx.Lock()
-	defer d.mtx.Unlock()
-
-	_, ok := d.jobs[eval.JobID]
-	if !ok {
-		return jobstore.NewErrJobNotFound(eval.JobID)
-	}
-
-	_, ok = d.evaluations[eval.ID]
-	if ok {
-		return bacerrors.NewAlreadyExists(eval.ID, "Evaluation")
-	}
-
-	d.evaluations[eval.ID] = eval
-
-	d.triggerEvent(jobstore.EvaluationWatcher, jobstore.CreateEvent, eval)
-
-	return nil
-}
-
-// GetEvaluation retrieves the specified evaluation
-func (d *InMemoryJobStore) GetEvaluation(ctx context.Context, id string) (models.Evaluation, error) {
-	d.mtx.RLock()
-	defer d.mtx.RUnlock()
-
-	ev, ok := d.evaluations[id]
-	if !ok {
-		returnError := bacerrors.NewEvaluationNotFound(id)
-		return models.Evaluation{}, returnError
-	}
-
-	return ev, nil
-}
-
-// DeleteEvaluation deletes the specified evaluation
-func (d *InMemoryJobStore) DeleteEvaluation(ctx context.Context, id string) error {
-	d.mtx.Lock()
-	defer d.mtx.Unlock()
-
-	d.triggerEvent(jobstore.EvaluationWatcher, jobstore.DeleteEvent, d.evaluations[id])
-
-	delete(d.evaluations, id)
-	return nil
-}
-
-func (d *InMemoryJobStore) Close(_ context.Context) error {
-	return nil
-}
-
-func (d *InMemoryJobStore) appendJobHistory(updateJob models.Job, previousState models.JobStateType, comment string) {
-	historyEntry := models.JobHistory{
-		Type:  models.JobHistoryTypeJobLevel,
-		JobID: updateJob.ID,
-		JobState: &models.StateChange[models.JobStateType]{
-			Previous: previousState,
-			New:      updateJob.State.StateType,
-		},
-		NewRevision: updateJob.Revision,
-		Comment:     comment,
-		Time:        time.Unix(0, updateJob.ModifyTime),
-	}
-	d.history[updateJob.ID] = append(d.history[updateJob.ID], historyEntry)
-}
-
-func (d *InMemoryJobStore) appendExecutionHistory(updatedExecution models.Execution,
-	previousState models.ExecutionStateType, comment string) {
-	historyEntry := models.JobHistory{
-		Type:        models.JobHistoryTypeExecutionLevel,
-		JobID:       updatedExecution.JobID,
-		NodeID:      updatedExecution.NodeID,
-		ExecutionID: updatedExecution.ID,
-		ExecutionState: &models.StateChange[models.ExecutionStateType]{
-			Previous: previousState,
-			New:      updatedExecution.ComputeState.StateType,
-		},
-		NewRevision: updatedExecution.Revision,
-		Comment:     comment,
-		Time:        time.Unix(0, updatedExecution.ModifyTime),
-	}
-	d.history[updatedExecution.JobID] = append(d.history[updatedExecution.JobID], historyEntry)
-}
-
-// Static check to ensure that InMemoryJobStore implements jobstore.Store
-var _ jobstore.Store = (*InMemoryJobStore)(nil)
diff --git a/pkg/repo/job_store.go b/pkg/repo/job_store.go
index b186cb3cb0..b41b208054 100644
--- a/pkg/repo/job_store.go
+++ b/pkg/repo/job_store.go
@@ -10,7 +10,6 @@ import (
 	"github.com/bacalhau-project/bacalhau/pkg/config/types"
 	"github.com/bacalhau-project/bacalhau/pkg/jobstore"
 	boltjobstore "github.com/bacalhau-project/bacalhau/pkg/jobstore/boltdb"
-	memjobstore "github.com/bacalhau-project/bacalhau/pkg/jobstore/inmemory"
 	"github.com/rs/zerolog/log"
 )
 
@@ -44,9 +43,6 @@ func (fsr *FsRepo) InitJobStore(ctx context.Context, prefix string) (jobstore.St
 
 		log.Ctx(ctx).Debug().Str("Path", path).Msg("creating boltdb backed jobstore")
 		return boltjobstore.NewBoltJobStore(path)
-	case types.InMemory:
-		log.Ctx(ctx).Debug().Msg("creating inmemory backed jobstore")
-		return memjobstore.NewInMemoryJobStore(), nil
 	default:
 		return nil, fmt.Errorf("unknown JobStore type: %s", storeCfg.Type)
 	}


Test patch:
diff --git a/pkg/jobstore/inmemory/inmemory_test.go b/pkg/jobstore/inmemory/inmemory_test.go
deleted file mode 100644
index 2a36ee71fe..0000000000
--- a/pkg/jobstore/inmemory/inmemory_test.go
+++ /dev/null
@@ -1,563 +0,0 @@
-//go:build unit || !integration
-
-package inmemory
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"sort"
-	"testing"
-	"time"
-
-	"github.com/bacalhau-project/bacalhau/pkg/test/mock"
-	"github.com/google/uuid"
-	"github.com/samber/lo"
-	"github.com/stretchr/testify/require"
-	"github.com/stretchr/testify/suite"
-	"k8s.io/apimachinery/pkg/labels"
-
-	"github.com/benbjohnson/clock"
-
-	"github.com/bacalhau-project/bacalhau/pkg/jobstore"
-	"github.com/bacalhau-project/bacalhau/pkg/models"
-)
-
-type InMemoryTestSuite struct {
-	suite.Suite
-	store *InMemoryJobStore
-	ctx   context.Context
-	clock *clock.Mock
-	ids   []string
-}
-
-func TestInMemoryTestSuite(t *testing.T) {
-	suite.Run(t, new(InMemoryTestSuite))
-}
-
-func (s *InMemoryTestSuite) SetupTest() {
-	s.clock = clock.NewMock()
-
-	s.store = NewInMemoryJobStore(WithClock(s.clock))
-	s.ctx = context.Background()
-
-	jobFixtures := []struct {
-		id              string
-		client          string
-		tags            map[string]string
-		jobStates       []models.JobStateType
-		executionStates []models.ExecutionStateType
-	}{
-		{
-			id:              "11111111-1111-1111-1111-111111111111",
-			client:          "client1",
-			tags:            map[string]string{"gpu": "true", "fast": "true"},
-			jobStates:       []models.JobStateType{models.JobStateTypePending, models.JobStateTypeRunning, models.JobStateTypeStopped},
-			executionStates: []models.ExecutionStateType{models.ExecutionStateAskForBid, models.ExecutionStateAskForBidAccepted, models.ExecutionStateCancelled},
-		},
-		{
-			id:              "22222222-2222-2222-2222-222222222222",
-			client:          "client2",
-			tags:            map[string]string{},
-			jobStates:       []models.JobStateType{models.JobStateTypePending, models.JobStateTypeRunning, models.JobStateTypeStopped},
-			executionStates: []models.ExecutionStateType{models.ExecutionStateAskForBid, models.ExecutionStateAskForBidAccepted, models.ExecutionStateCancelled},
-		},
-		{
-			id:              "33333333-3333-3333-3333-333333333333",
-			client:          "client3",
-			tags:            map[string]string{"slow": "true", "max": "10"},
-			jobStates:       []models.JobStateType{models.JobStateTypePending, models.JobStateTypeRunning},
-			executionStates: []models.ExecutionStateType{models.ExecutionStateAskForBid, models.ExecutionStateAskForBidAccepted},
-		},
-		{
-			id:              "44444444-4444-4444-4444-444444444444",
-			client:          "client4",
-			tags:            map[string]string{"max": "10"},
-			jobStates:       []models.JobStateType{models.JobStateTypePending, models.JobStateTypeRunning},
-			executionStates: []models.ExecutionStateType{models.ExecutionStateAskForBid, models.ExecutionStateAskForBidAccepted},
-		},
-		{
-			id:              "55555555-5555-5555-5555-555555555555",
-			client:          "client5",
-			tags:            map[string]string{"max": "10"},
-			jobStates:       []models.JobStateType{models.JobStateTypePending, models.JobStateTypeRunning},
-			executionStates: []models.ExecutionStateType{models.ExecutionStateAskForBid, models.ExecutionStateAskForBidAccepted},
-		},
-	}
-
-	for _, fixture := range jobFixtures {
-		s.ids = append(s.ids, fixture.id)
-
-		s.clock.Add(1 * time.Second)
-		job := makeDockerEngineJob(
-			[]string{"bash", "-c", "echo hello"})
-
-		job.ID = fixture.id
-		job.Labels = fixture.tags
-		job.Namespace = fixture.client
-
-		err := s.store.CreateJob(s.ctx, *job)
-		s.NoError(err)
-
-		s.clock.Add(1 * time.Second)
-
-		execution := mock.ExecutionForJob(job)
-		err = s.store.CreateExecution(s.ctx, *execution)
-		s.NoError(err)
-
-		for i, state := range fixture.jobStates {
-			s.clock.Add(1 * time.Second)
-
-			oldState := models.JobStateTypePending
-			if i > 0 {
-				oldState = fixture.jobStates[i-1]
-			}
-
-			request := jobstore.UpdateJobStateRequest{
-				JobID:    fixture.id,
-				NewState: state,
-				Condition: jobstore.UpdateJobCondition{
-					ExpectedState:    oldState,
-					ExpectedRevision: uint64(i + 1),
-				},
-				Comment: fmt.Sprintf("moved to %+v", state),
-			}
-			err = s.store.UpdateJobState(s.ctx, request)
-			s.NoError(err)
-		}
-
-		for i, state := range fixture.executionStates {
-			s.clock.Add(1 * time.Second)
-
-			oldState := models.ExecutionStateNew
-			if i > 0 {
-				oldState = fixture.executionStates[i-1]
-			}
-
-			// We are pretending this is a new execution struct
-			execution.ComputeState.StateType = state
-			execution.ModifyTime = s.clock.Now().UTC().UnixNano()
-
-			request := jobstore.UpdateExecutionRequest{
-				ExecutionID: execution.ID,
-				Condition: jobstore.UpdateExecutionCondition{
-					ExpectedStates:   []models.ExecutionStateType{oldState},
-					ExpectedRevision: uint64(i + 1),
-				},
-				NewValues: *execution,
-				Comment:   fmt.Sprintf("exec update to %+v", state),
-			}
-
-			err = s.store.UpdateExecution(s.ctx, request)
-			s.NoError(err)
-		}
-
-	}
-}
-
-func (s *InMemoryTestSuite) TearDownTest() {
-	s.store.Close(s.ctx)
-	s.ids = []string{}
-}
-
-func (s *InMemoryTestSuite) TestUnfilteredJobHistory() {
-	history, err := s.store.GetJobHistory(s.ctx, s.ids[0], jobstore.JobHistoryFilterOptions{})
-	require.NoError(s.T(), err, "failed to get job history")
-	require.Equal(s.T(), 8, len(history))
-}
-
-func (s *InMemoryTestSuite) TestJobHistoryOrdering() {
-	history, err := s.store.GetJobHistory(s.ctx, s.ids[0], jobstore.JobHistoryFilterOptions{})
-	require.NoError(s.T(), err, "failed to get job history")
-
-	// There are 6 history entries that we created directly, and 2 created by
-	// CreateJob and CreateExecution
-	require.Equal(s.T(), 8, len(history))
-
-	// Make sure they come back in order
-	values := make([]int64, len(history))
-	for i, h := range history {
-		values[i] = h.Time.Unix()
-	}
-
-	require.Equal(s.T(), []int64{1, 2, 3, 4, 5, 6, 7, 8}, values)
-}
-
-func (s *InMemoryTestSuite) TestTimeFilteredJobHistory() {
-	options := jobstore.JobHistoryFilterOptions{
-		Since: 5,
-	}
-
-	history, err := s.store.GetJobHistory(s.ctx, s.ids[0], options)
-	require.NoError(s.T(), err, "failed to get job history")
-	require.Equal(s.T(), 4, len(history))
-}
-
-func (s *InMemoryTestSuite) TestLevelFilteredJobHistory() {
-	jobOptions := jobstore.JobHistoryFilterOptions{
-		ExcludeExecutionLevel: true,
-	}
-	execOptions := jobstore.JobHistoryFilterOptions{
-		ExcludeJobLevel: true,
-	}
-
-	history, err := s.store.GetJobHistory(s.ctx, s.ids[0], jobOptions)
-	s.NoError(err, "failed to get job history")
-	s.Equal(4, len(history))
-	s.Equal(models.JobStateTypePending, history[1].JobState.New)
-
-	count := lo.Reduce(history, func(agg int, item models.JobHistory, _ int) int {
-		if item.Type == models.JobHistoryTypeJobLevel {
-			return agg + 1
-		}
-		return agg
-	}, 0)
-	s.Equal(count, 4)
-
-	history, err = s.store.GetJobHistory(s.ctx, s.ids[0], execOptions)
-	s.NoError(err, "failed to get job history")
-	s.Equal(4, len(history))
-	s.Equal(models.ExecutionStateAskForBid, history[1].ExecutionState.New)
-
-	count = lo.Reduce(history, func(agg int, item models.JobHistory, _ int) int {
-		if item.Type == models.JobHistoryTypeExecutionLevel {
-			return agg + 1
-		}
-		return agg
-	}, 0)
-	s.Equal(count, 4)
-}
-
-func (s *InMemoryTestSuite) TestSearchJobs() {
-	s.T().Run("by client ID and included tags", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			Namespace:   "client1",
-			IncludeTags: []string{"fast", "slow"},
-		})
-		require.NoError(t, err)
-		jobs := response.Jobs
-		require.Equal(t, 1, len(jobs))
-		require.Equal(t, "client1", jobs[0].Namespace)
-		require.Contains(t, jobs[0].Labels, "fast")
-		require.NotContains(t, jobs[0].Labels, "slow")
-	})
-
-	s.T().Run("simple selectors", func(t *testing.T) {
-		// Get the first job, which we expect to have the selector succeed with
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			Namespace: "client1",
-			Selector:  s.parseLabels("gpu=true,fast=true"),
-		})
-		require.NoError(t, err)
-		jobs := response.Jobs
-		require.Equal(t, 1, len(jobs))
-		require.Equal(t, "client1", jobs[0].Namespace)
-	})
-
-	s.T().Run("all records with selectors", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			Selector: s.parseLabels("max>1"),
-			Limit:    2,
-		})
-		require.NoError(t, err)
-		require.Equal(t, 2, len(response.Jobs))
-	})
-
-	s.T().Run("all records with selectors and paging", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			SortBy:   "created_at",
-			Selector: s.parseLabels("max>1"),
-			Limit:    2,
-		})
-		require.NoError(t, err)
-		jobs := response.Jobs
-		require.Equal(t, 2, len(jobs))
-
-		// Having skipped the first two s.ids because of non-matching selectors,
-		// we expect the next two to match
-		require.Equal(t, s.ids[2], jobs[0].ID)
-		require.Equal(t, s.ids[3], jobs[1].ID)
-
-		response, err = s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			SortBy:   "created_at",
-			Selector: s.parseLabels("max>1"),
-			Limit:    2,
-			Offset:   2,
-		})
-
-		require.NoError(t, err)
-		jobs = response.Jobs
-		require.Equal(t, 1, len(jobs))
-		require.Equal(t, s.ids[4], jobs[0].ID)
-	})
-
-	s.T().Run("everything sorted by created_at", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			ReturnAll: true,
-		})
-		require.NoError(t, err)
-		jobs := response.Jobs
-		require.Equal(t, 5, len(jobs))
-
-		ids := lo.Map(jobs, func(item models.Job, _ int) string {
-			return item.ID
-		})
-
-		require.EqualValues(t, s.ids, ids)
-
-		response, err = s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			ReturnAll:   true,
-			SortReverse: true,
-		})
-		require.NoError(t, err)
-		jobs = response.Jobs
-		require.Equal(t, 5, len(jobs))
-
-		ids = lo.Map(jobs, func(item models.Job, _ int) string {
-			return item.ID
-		})
-
-		working := append([]string(nil), s.ids...)
-		reversedOriginals := lo.Reverse(working)
-
-		require.EqualValues(t, reversedOriginals, ids)
-	})
-
-	s.T().Run("everything", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			ReturnAll: true,
-		})
-		require.NoError(t, err)
-		require.Equal(t, 5, len(response.Jobs))
-	})
-
-	s.T().Run("everything offset", func(t *testing.T) {
-		// Offset ignored inmemorystore
-	})
-
-	s.T().Run("everything limit", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			ReturnAll: true,
-			Limit:     2,
-		})
-		require.NoError(t, err)
-		require.Equal(t, 2, len(response.Jobs))
-	})
-
-	s.T().Run("everything offset/limit", func(t *testing.T) {
-		// Offset ignored inmemorystore
-	})
-
-	s.T().Run("include tags", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			IncludeTags: []string{"gpu"},
-		})
-
-		require.NoError(t, err)
-		require.Equal(t, 1, len(response.Jobs))
-		require.Equal(t, s.ids[0], response.Jobs[0].ID)
-	})
-
-	s.T().Run("all but exclude tags", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			ReturnAll:   true,
-			ExcludeTags: []string{"fast"},
-		})
-		require.NoError(t, err)
-		require.Equal(t, 4, len(response.Jobs))
-	})
-
-	s.T().Run("include-exclude same tag", func(t *testing.T) {
-		response, err := s.store.GetJobs(s.ctx, jobstore.JobQuery{
-			IncludeTags: []string{"gpu"},
-			ExcludeTags: []string{"fast"},
-		})
-		require.NoError(t, err)
-		require.Equal(t, 0, len(response.Jobs))
-	})
-}
-
-func (s *InMemoryTestSuite) TestDeleteJob() {
-	job := makeDockerEngineJob(
-		[]string{"bash", "-c", "echo hello"})
-	job.Labels = map[string]string{"tag": "value"}
-	job.ID = "deleteme"
-	job.Namespace = "client1"
-	err := s.store.CreateJob(s.ctx, *job)
-	s.NoError(err)
-
-	err = s.store.DeleteJob(s.ctx, job.ID)
-	s.NoError(err)
-}
-
-func (s *InMemoryTestSuite) TestGetJob() {
-	job, err := s.store.GetJob(s.ctx, s.ids[0])
-	s.NoError(err)
-	s.NotNil(job)
-
-	_, err = s.store.GetJob(s.ctx, "100")
-	s.Error(err)
-}
-
-func (s *InMemoryTestSuite) TestGetExecutions() {
-	id := s.ids[0]
-	_, err := s.store.GetJob(s.ctx, id)
-	s.NoError(err)
-
-	state, err := s.store.GetExecutions(s.ctx, id)
-	s.NoError(err)
-	s.NotNil(state)
-	s.Greater(len(state), 0)
-
-	_, err = s.store.GetExecutions(s.ctx, uuid.New().String())
-	s.Error(err)
-}
-
-func (s *InMemoryTestSuite) TestInProgressJobs() {
-	jobs, err := s.store.GetInProgressJobs(s.ctx)
-	s.NoError(err)
-	s.Equal(3, len(jobs))
-
-	sourceIDs := s.ids[2:]
-	sort.Strings(sourceIDs)
-
-	jobIDs := lo.Map(jobs, func(item models.Job, _ int) string { return item.ID })
-	sort.Strings(jobIDs)
-
-	s.Equal(sourceIDs, jobIDs)
-}
-
-func (s *InMemoryTestSuite) TestEvents() {
-	ch := s.store.Watch(s.ctx,
-		jobstore.JobWatcher|jobstore.ExecutionWatcher,
-		jobstore.CreateEvent|jobstore.UpdateEvent|jobstore.DeleteEvent,
-	)
-
-	job := makeDockerEngineJob(
-		[]string{"bash", "-c", "echo hello"})
-	job.ID = "10"
-	job.Namespace = "1"
-
-	var execution models.Execution
-
-	s.Run("job create event", func() {
-		err := s.store.CreateJob(s.ctx, *job)
-		s.NoError(err)
-
-		// Read an event, it should be a jobcreate
-		ev := <-ch
-		s.Equal(ev.Event, jobstore.CreateEvent)
-		s.Equal(ev.Kind, jobstore.JobWatcher)
-
-		var decodedJob models.Job
-		err = json.Unmarshal(ev.Object, &decodedJob)
-		s.NoError(err)
-		s.Equal(decodedJob.ID, job.ID)
-	})
-
-	s.Run("execution create event", func() {
-		s.clock.Add(1 * time.Second)
-		execution = models.Execution{
-			JobID:        "10",
-			NodeID:       "nodeid",
-			ID:           "e-computeRef",
-			ComputeState: models.State[models.ExecutionStateType]{StateType: models.ExecutionStateNew},
-		}
-		err := s.store.CreateExecution(s.ctx, execution)
-		s.NoError(err)
-
-		// Read an event, it should be a ExecutionForJob Create
-		ev := <-ch
-		s.Equal(ev.Event, jobstore.CreateEvent)
-		s.Equal(ev.Kind, jobstore.ExecutionWatcher)
-	})
-
-	s.Run("update job state event", func() {
-		request := jobstore.UpdateJobStateRequest{
-			JobID:    "10",
-			NewState: models.JobStateTypeRunning,
-			Condition: jobstore.UpdateJobCondition{
-				ExpectedState: models.JobStateTypePending,
-			},
-			Comment: "event test",
-		}
-		_ = s.store.UpdateJobState(s.ctx, request)
-		ev := <-ch
-		s.Equal(ev.Event, jobstore.UpdateEvent)
-		s.Equal(ev.Kind, jobstore.JobWatcher)
-	})
-
-	s.Run("update execution state event", func() {
-		execution.ComputeState.StateType = models.ExecutionStateAskForBid
-		execution.ModifyTime = s.clock.Now().UTC().UnixNano()
-		s.store.UpdateExecution(s.ctx, jobstore.UpdateExecutionRequest{
-			ExecutionID: execution.ID,
-			Condition: jobstore.UpdateExecutionCondition{
-				ExpectedStates: []models.ExecutionStateType{models.ExecutionStateNew},
-			},
-			NewValues: execution,
-			Comment:   "event test",
-		})
-		ev := <-ch
-		s.Equal(ev.Event, jobstore.UpdateEvent)
-		s.Equal(ev.Kind, jobstore.ExecutionWatcher)
-
-		var decodedExecution models.Execution
-		err := json.Unmarshal(ev.Object, &decodedExecution)
-		s.NoError(err)
-		s.Equal(decodedExecution.ID, execution.ID)
-	})
-
-	s.Run("delete job event", func() {
-		_ = s.store.DeleteJob(s.ctx, job.ID)
-		ev := <-ch
-		s.Equal(ev.Event, jobstore.DeleteEvent)
-		s.Equal(ev.Kind, jobstore.JobWatcher)
-	})
-}
-
-func (s *InMemoryTestSuite) TestEvaluations() {
-
-	eval := models.Evaluation{
-		ID:    "e1",
-		JobID: "10",
-	}
-
-	// Wrong job ID means JobNotFound
-	err := s.store.CreateEvaluation(s.ctx, eval)
-	s.Error(err)
-
-	// Correct job ID
-	eval.JobID = s.ids[0]
-	err = s.store.CreateEvaluation(s.ctx, eval)
-	s.NoError(err)
-
-	_, err = s.store.GetEvaluation(s.ctx, "missing")
-	s.Error(err)
-
-	e, err := s.store.GetEvaluation(s.ctx, eval.ID)
-	s.NoError(err)
-	s.Equal(e, eval)
-
-	err = s.store.DeleteEvaluation(s.ctx, eval.ID)
-	s.NoError(err)
-}
-
-func (s *InMemoryTestSuite) parseLabels(selector string) labels.Selector {
-	req, err := labels.ParseToRequirements(selector)
-	s.NoError(err)
-
-	return labels.NewSelector().Add(req...)
-}
-
-func makeDockerEngineJob(entrypointArray []string) *models.Job {
-	j := mock.Job()
-	j.Task().Engine = &models.SpecConfig{
-		Type: models.EngineDocker,
-		Params: map[string]interface{}{
-			"Image":      "ubuntu:latest",
-			"Entrypoint": entrypointArray,
-		},
-	}
-	return j
-}
diff --git a/pkg/orchestrator/endpoint_test.go b/pkg/orchestrator/endpoint_test.go
index 9e5addf494..a4ec8f351a 100644
--- a/pkg/orchestrator/endpoint_test.go
+++ b/pkg/orchestrator/endpoint_test.go
@@ -15,7 +15,7 @@ import (
 	"github.com/bacalhau-project/bacalhau/pkg/config/types"
 	"github.com/bacalhau-project/bacalhau/pkg/eventhandler"
 	"github.com/bacalhau-project/bacalhau/pkg/ipfs"
-	"github.com/bacalhau-project/bacalhau/pkg/jobstore/inmemory"
+	boltjobstore "github.com/bacalhau-project/bacalhau/pkg/jobstore/boltdb"
 	"github.com/bacalhau-project/bacalhau/pkg/models"
 	"github.com/bacalhau-project/bacalhau/pkg/node"
 	"github.com/bacalhau-project/bacalhau/pkg/orchestrator"
@@ -74,10 +74,18 @@ func (s *EndpointSuite) TestInlinePinnerTransformInSubmit() {
 	evalBroker := orchestrator.NewMockEvaluationBroker(ctrl)
 	evalBroker.EXPECT().Enqueue(gomock.Any()).Return(nil)
 
+	tmpFile := filepath.Join(s.T().TempDir(), "test.db")
+	jobstore, err := boltjobstore.NewBoltJobStore(tmpFile)
+	defer func() {
+		jobstore.Close(s.ctx)
+		os.Remove(tmpFile)
+	}()
+	s.Require().NoError(err)
+
 	endpoint := orchestrator.NewBaseEndpoint(&orchestrator.BaseEndpointParams{
 		ID:               "test_endpoint",
 		EvaluationBroker: evalBroker,
-		Store:            inmemory.NewInMemoryJobStore(),
+		Store:            jobstore,
 		EventEmitter:     eventEmitter,
 		JobTransformer: transformer.ChainedTransformer[*models.Job]{
 			transformer.JobFn(transformer.IDGenerator),