On this page:
20.1 Workspaces
workspace-directory/  c
DENXI_  WORKSPACE
build-workspace-path
call-with-ephemeral-workspace
path-in-workspace?
make-addressable-file
make-addressable-directory
make-addressable-link
20.2 Databases
20.2.1 Record Types
record
provider-record
package-record
edition-record
revision-record
revision-name-record
path-record
path-key-record
output-record
20.2.2 High-Level Queries
in-all-installed
declare-link
declare-output
find-path-record
call-with-reused-output
in-denxi-objects
in-denxi-outputs
start-transaction!
build-object-path
build-addressable-path
in-issued-links
20.3 Garbage Collection
$finished-collecting-garbage
denxi-collect-garbage
20.4 Content Addressing
make-content-address
current-content-scanner
scan-all-filesystem-content
$no-content-to-address
8.12

20 State🔗ℹ

 (require denxi/state) package: denxi

Denxi’s state consists of the contents of a filesystem directory, and a database in that directory. denxi/state encapsulates state I/O.

Denxi implicitly trusts a state, so directly tampering with state is a bad idea. Only use denxi/state to interact with a state.

20.1 Workspaces🔗ℹ

A workspace is a directory on the filesystem that holds a database and all installed software. A workspace’s path must match the workspace-directory/c contract. A target workspace is the directory referenced by the value of (DENXI_WORKSPACE). Target workspaces are affected by all filesystem writes in a Denxi process.

value

workspace-directory/c : contract?

 = 
(and/c complete-path?
       (or/c directory-exists?
             (and/c (not/c file-exists?)
                    (not/c directory-exists?)
                    (not/c link-exists?))))
A contract for a valid workspace directory path.

That is, a complete path that either refers to an existing directory, or a location on the filesystem where nothing exists.

CLI Flags: --w/--workspace

The directory in which Denxi reads and writes files. If the directory does not exist, then it will be created when Denxi writes a file.

Defaults to (build-path (find-system-path 'home-dir) ".denxi").

procedure

(build-workspace-path path-element)  complete-path?

  path-element : (and/c path-string? (not/c complete-path?))
Like build-path, but the base of the returned path is (DENXI_WORKSPACE).

procedure

(call-with-ephemeral-workspace proc)  any

  proc : (-> path? any)
Calls proc in a parameterization where (DENXI_WORKSPACE) is a temporary directory. The same path is passed as the first argument to proc. That directory and its contents will be deleted when control leaves proc, if it still exists.

procedure

(path-in-workspace? path)  boolean?

  path : path-string?
Returns #t if path, when simplified, has (DENXI_WORKSPACE) as a prefix.

procedure

(make-addressable-file transfer-name 
  in 
  est-size 
  #:on-status on-status 
  #:max-size max-size 
  #:buffer-size buffer-size 
  #:timeout-ms timeout-ms 
  [#:cache-key cache-key]) 
  path-record?
  transfer-name : non-empty-string?
  in : input-port?
  est-size : (or/c +inf.0 exact-positive-integer?)
  on-status : (-> $message? any)
  max-size : (or/c +inf.0 exact-positive-integer?)
  buffer-size : exact-positive-integer?
  timeout-ms : (>=/c 0)
  cache-key : (or/c bytes? #f) = #f
Called for effect. Returns a path-record for a file.

Effect: Atomically create OR reuse a file in the workspace.

If cache-key is not #f, then make-addressable-file first attempts to find an existing path-record using cache-key. If one is found, make-addressable-file will return that record.

In the event of a cache miss, a file is created under the context of several safety limits. An output port to-file is then given bytes under the context of

(transfer in to-file
  #:on-status on-status
  #:transfer-name transfer-name
  #:max-size max-size
  #:buffer-size buffer-size
  #:timeout-ms timeout-ms
  #:est-size est-size)

In the event of any error, the created file is deleted if it exists, and the database will not be affected. If the file is created successfully, then the database will gain a path-record. If cache-key is not #f, the database will also gain a path-key-record.

procedure

(make-addressable-directory directory)  path-record?

  directory : directory-exists?
Called for effect. Returns a record for a created directory.

Effect: Atomically move directory to a path named after the Base32 encoding of a digest. The digest is created using the directory’s own name and contents, discovered recursively. The database will gain a record of the new directory path, which will be returned.

Warning: Any digests that are already computed for a path are preferred over creating new digests from contents on disk. In the event a file is directly modified in the workspace, a digest for a directory containing that file may be incorrect.

procedure

(make-addressable-link target-path-record    
  link-path)  path-record?
  target-path-record : path-record?
  link-path : path-string?
Called for effect. Returns a record for a created link.

Effect: Creates the directory path before the file name in link-path (if necessary), then creates a link at link-path. The net operation is performed non-atomically.

The target path must come from a valid path-record because the links may only point to files and directories created by Denxi.

20.2 Databases🔗ℹ

Each workspace may contain a database as a SQLite file called db. The database tracks the other contents of a workspace, and holds the state of any caches.

20.2.1 Record Types🔗ℹ

This section covers the Racket structures representing parsed records in a relation. To inspect the underlying database schema, run sqlite3 ./path/to/db in your shell, then .schema in SQLite’s REPL.

struct

(struct record (id)
    #:extra-constructor-name make-record
    #:transparent)
  id : (or/c exact-positive-integer? #f sql-null?)
Represents an identifiable database record.

struct

(struct provider-record record (name))

  name : non-empty-string?
Represents a known provider. One provider-record has many package-records.

struct

(struct package-record record (name provider-id))

  name : non-empty-string?
  provider-id : exact-positive-integer?
Represents a known installed package. One package-record has many edition-records.

struct

(struct edition-record record (name package-id))

  name : non-empty-string?
  package-id : exact-positive-integer?
Represents a known edition. One edition-record has many revision-records.

struct

(struct revision-record record (number edition-id))

  number : revision-number?
  edition-id : exact-positive-integer?
Represents a known revision number under an edition. One revision-record has many revision-name-records.

number is a revision number, not a record-id. edition-id is a record-id for an edition-record.

struct

(struct revision-name-record record (name revision-id))

  name : non-empty-string?
  revision-id : exact-positive-integer?
Represents a name for a revision number.

name need not be unique in the relation.

revision-id is unique, and represents the record-id of a revision-record, not a revision number.

struct

(struct path-record record (path digest target-id)
    #:extra-constructor-name make-path-record)
  path : (and/c path-string? (not/c complete-path?))
  digest : bytes?
  target-id : exact-positive-integer?
Represents a workspace-relative path that is expected to follow these invariants:

A path-record has many path-key-records.

struct

(struct path-key-record record (key path-id))

  key : bytes?
  path-id : exact-positive-integer?
Represents a cached path used by make-addressable-file.

key must be unique.

path-id is a record-id of a path-record.

struct

(struct output-record record (revision-id path-id name))

  revision-id : exact-positive-integer?
  path-id : exact-positive-integer?
  name : non-empty-string?
Represents an installed package output. output-records have a 1-to-1 relationship with path-records, and can be used to query all other record types that identify an installed package.

path-id refers to the path-record for the package output. It must be unique.

name refers to the name of the output. It need not be unique.

20.2.2 High-Level Queries🔗ℹ

procedure

(in-all-installed)  sequence?

Returns a 12-value sequence representing discovery information and file location information for all package outputs installed in the target workspace.

The values are always correct together, but do not guarentee the existence of a path on disk. Ignoring the record-ids, each set of values represents a path to a named package output for a specific revision, edition, package, and provider.

procedure

(declare-link link-path target-record)  path-record?

  link-path : path-string?
  target-record : path-record?
Saves an existing link at link-path as pointing to the (presumably existing) path in target-record. If the link already exists in the database, the existing record is returned.

After using declare-link, the path in target-record becomes eligible for garbage collection when (not (link-exists? link-path)).

procedure

(declare-output provider-name    
  package-name    
  edition-name    
  revision-number    
  revision-names    
  output-name    
  output-path-record)  output-record?
  provider-name : non-empty-string?
  package-name : non-empty-string?
  edition-name : non-empty-string?
  revision-number : revision-number?
  revision-names : (listof non-empty-string?)
  output-name : string?
  output-path-record : path-record?
Saves the relationship between the package output named output-name, the given discovery and version information, and the given path record.

procedure

(find-path-record hint)  (or/c path-record? #f)

  hint : any/c
Return a path-record produced from searching the database using the given hint, or #f if no record was found.

The database query used depends on the type of hint.

procedure

(call-with-reused-output query    
  output-name    
  continue)  any
  query : package-query-variant?
  output-name : string?
  continue : (-> (or/c #f exn? output-record?) any)
Returns (continue variant), where variant comes from a search for an output named output-name for a package matching query. The exact package used has the highest revision number available in the database.

If variant is #f, then the named output is not installed.

If variant is an output-record?, then the named output is installed with that information.

If variant is an exn?, then an error was encountered in preparing or executing a SQL query.

procedure

(in-denxi-objects query)  
(sequence/c path-string?
            exact-positive-integer?
            revision-number?
            exact-positive-integer?
            path-string?)
  query : package-query-variant?
Returns a 5-value sequence representing known installed outputs of packages matching query on the file system.

procedure

(in-denxi-outputs query output-name)

  (sequence/c output-record?)
  query : package-query-variant?
  output-name : string?
Returns a sequence of output-record for outputs of the given output-name, for all installed packages matching query.

procedure

(start-transaction!)  
(-> void?) (-> void?)
Called for effect. Returns two procedures also called for effect, discussed below.

Effect: start-transaction! instructs SQLite to start a transaction against the database. If a transaction is already underway, start-transaction! will handle the corresponding error from SQLite by using the existing transaction.

The first returned procedure ends the transaction, atomically committing any changes to the database.

The second returned procedure rolls back the transaction, atomically undoing any changes to the database.

Failure to call either procedure will leave the transaction open. For safety, use the returned procedures such that the program cannot help but invoke one.

procedure

(build-object-path path-el ...)  complete-path?

  path-el : (and/c path-string? (not/c complete-path?))
Equivalent to (build-workspace-path "objects" path-el ...).

procedure

(build-addressable-path digest)  complete-path?

  digest : bytes?
Like build-object-path, but the file name is (encoded-file-name digest).

Returns a sequence of two values.

The first is a workspace-relative path to a symbolic link created by Denxi.

The second is a workspace-relative path to the file referenced by the link.

20.3 Garbage Collection🔗ℹ

struct

(struct $finished-collecting-garbage $message (bytes-recovered)
    #:prefab)
  bytes-recovered : exact-nonnegative-integer?
A message for reporting the number of bytes freed from disk using denxi-collect-garbage.

Deletes all records of links that do not actually exist on disk, then deletes any installed files or directories in the target workspace with no known links.

Returns the estimated number of bytes recovered from disk. Empty directories and links are assumed to have no size.

20.4 Content Addressing🔗ℹ

Denxi addresses arbitrary data using digests. One may override how data appears when computing new addresses.

Returns a digest suitable for use in path-record-digest.

The digest always uses (get-default-chf), but the bytes used to produce the digest depend on current-content-scanner.

A parameter used to store a procedure.

The procedure must accept a path-string? and return an input port. The input port yields bytes that act as representitive content for whatever exists at that location.

Defaults to scan-all-filesystem-content.

procedure

(scan-all-filesystem-content path)  input-port?

  path : path-string?
Returns an input port that draws all accessible data (including some platform-specific data) from an existing file, directory, or link at path according to the rules below. The rules are checked in the order shown.

Warning: Large directories and files may take a long time to process. Only use when change detection is more important than performance.

Note: When this definition refers to a “name”, it means the bytes in (string->bytes/utf-8 (~a (file-name-from-path path))).

If (link-exists? path), then return a port that yields bytes from the link’s name. The link is not followed.

If (file-exists? path), then return a port that yields bytes from the file’s name, permissions, and contents.

If (directory-exists? path), then return a port that yields bytes from the directory’s name, permissions, and contents. Contents are processed recursively without tail-call optimization.

Otherwise, raise ($no-content-to-address path).

When trying to create a digest to address content, path did not point to an existing file, directory, or link.