Skip to main content

IStateProvider

Where Octo's state files live.

Octo has no database - it records what it built in state files, and reads them back on the next run to work out the diff. A state provider is only asked to put a named blob somewhere and hand it back later, so implementing this interface is all it takes to keep state in S3, a database, or anywhere else your team already trusts.

Pass one to Octo.initialize.

@group

Services/State Management

@see

Techniques for State Providers.

Implemented by

Index

Methods

getAppLock

  • getAppLock(): Promise<string | undefined>
  • The id of the lock currently held, or undefined when the app is unlocked.


    Returns Promise<string | undefined>

getSealedStates

  • getSealedStates(stateFileNames: string[]): Promise<(Buffer<ArrayBufferLike> | undefined)[]>
  • Reads state files from the newest sealed state, which a released lock left behind, all from the one seal. Another app reads this one's state here, never while one of its runs is half done.

    Returns one entry per name in the order given, undefined for a file the sealed state does not hold. All are undefined when nothing is sealed yet.


    Parameters

    • stateFileNames: string[]

    Returns Promise<(Buffer<ArrayBufferLike> | undefined)[]>

getState

  • getState(stateFileName: string): Promise<Buffer<ArrayBufferLike>>
  • Reads a state file.

    @throws

    TransactionError With the message No state found! when the file does not exist. Octo treats that as a first run rather than an error.


    Parameters

    • stateFileName: string

    Returns Promise<Buffer<ArrayBufferLike>>

getTemporaryFilePath

  • getTemporaryFilePath(name: string): Promise<string>
  • A new path where a writer prepares a file before saving it by its path, named after name. It is no state file, and the writer removes it if it never saves it.


    Parameters

    • name: string

    Returns Promise<string>

isAppLocked

  • isAppLocked(lockId: string): Promise<boolean>
  • Whether lockId is the lock currently held.

    @throws

    TransactionError With the message Invalid lock ID! when a different lock is held, so a caller holding a stale id is told it is stale rather than told the app is free.


    Parameters

    • lockId: string

    Returns Promise<boolean>

listStates

  • listStates(prefix: string): Promise<string[]>
  • The names of the state files whose name starts with prefix, named as IStateProvider.getState would be given them.

    Octo reads state by name everywhere else, so this is the only way it can discover a file it was not told about - a record an interrupted run left behind. Returns an empty list when the store holds nothing matching, including when it does not exist yet.


    Parameters

    • prefix: string

    Returns Promise<string[]>

lockApp

  • lockApp(lock: { commitId: string } | { lockId: string }): Promise<{ commitId?: string; lockId: string; resumedFrom?: directory | run | sealed }>
  • Takes the app lock and returns its id, which apply and commit must then present.

    The lock is taken atomically, so of two callers racing for it exactly one wins. commitId names what the run writes into and seals as, and starts with the time it was made. With lockId, resumes the run already holding that lock instead, which is how a run that died is finished.

    A provider sharing state names where a resume took the state directory from in resumedFrom: directory, the directory the lock was taken in, left as it was; run, what the run uploaded so far; sealed, the newest sealed state, because the run uploaded nothing.

    @throws

    AppLockError When a lock is held, naming the id that holds it. When lockId is given and is not the lock held.


    Parameters

    • lock: { commitId: string } | { lockId: string }
      • commitId: string
      • lockId: string

    Returns Promise<{ commitId?: string; lockId: string; resumedFrom?: directory | run | sealed }>

refreshState

  • refreshState(): Promise<{ commitId?: string; previousCommitId?: string; replaced: boolean }>
  • Brings the state up to the newest a run may plan against. generate calls it before reading any.

    A provider whose state is always the newest does nothing here, and returns replaced: false. One that replaced the state files names the commit it replaced them with, and the one before.


    Returns Promise<{ commitId?: string; previousCommitId?: string; replaced: boolean }>

saveStates

  • saveStates(stateFiles: ({ data: Buffer<ArrayBufferLike> | undefined; stateFileName: string } | { filePath: string; stateFileName: string })[]): Promise<void>
  • Writes state files as one change, overwriting whatever was there. A file comes from data, or is moved from filePath, a path IStateProvider.getTemporaryFilePath gave, without being read into memory; one given data: undefined is deleted. Deleting one that is not there is not an error.

    A provider that pays per write makes one write of the whole change rather than one per file.


    Parameters

    • stateFiles: ({ data: Buffer<ArrayBufferLike> | undefined; stateFileName: string } | { filePath: string; stateFileName: string })[]

    Returns Promise<void>

unlockApp

  • unlockApp(lockId: string): Promise<void>
  • Seals the state, then releases the app lock. Unlocking an app that is not locked is not an error.

    @throws

    TransactionError With the message Invalid lock ID! when a different lock is held, so one run can never release another's.


    Parameters

    • lockId: string

    Returns Promise<void>