INTEGRITY Dokumentace

Úložiště Durable Object podporované SQLite

Durable Object Storage API umožňuje Durable Objects k přístupu k transakčnímu a silně konzistentnímu úložišti. Úložiště připojené k Durable Object je soukromé pro danou jedinečnou instanci a jiné objekty k němu nemají přístup.

Durable Object Storage API nabízí několik metod, včetně SQL, obnovy k určitému bodu v čase (PITR), klíč-hodnota (KV) a alarmů. Dostupné metody API závisí na úložišti, které třída Durable Objects používá, buď SQLite nebo KV.

Metody 1 Třída Durable Object podporovaná SQLite Třída Durable Object podporovaná KV
SQL API ✅ ❌
PITR API ✅ ❌
Synchronní KV API ✅ 2, 3 ❌
Asynchronní KV API ✅ 3 ✅
Alarms API ✅ ✅

Poznámky pod čarou

1 Každá metoda je implicitně obalena transakcí, takže jsou její výsledky atomické a izolované od všech ostatních operací s úložištěm, a to i při přístupu k více párům klíč-hodnota.

2 Metody KV API jako get(), put(), delete(), nebo list() ukládají data do skryté tabulky SQLite __cf_kv. Mějte na paměti, že tuto tabulku uvidíte při výpisu všech tabulek, ale k jejímu obsahu se přes SQL API nedostanete.

3 Durable Objects s podporou SQLite také používají synchronní metody KV API pomocí ctx.storage.kv, zatímco Durable Objects s úložištěm KV poskytují pouze asynchronní metody KV API.

Přístup k úložišti

Durable Objects získávají přístup ke Storage API prostřednictvím DurableObjectStorage rozhraní a je přístupné přes DurableObjectState::storage vlastnost. K té se často přistupuje přes this.ctx.storage hodnotou ctx parametr předaný konstruktoru Durable Object.

Následující ukázka kódu ukazuje, jak pomocí Durable Object Storage API ukládat a načítat data.

export class Counter extends DurableObject {
	constructor(ctx, env) {
		super(ctx, env);
	}

	async increment() {
		let value = (await this.ctx.storage.get("value")) || 0;
		value += 1;
		await this.ctx.storage.put("value", value);
		return value;
	}
}
export class Counter extends DurableObject {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
  }

    async increment(): Promise<number> {
      let value: number = (await this.ctx.storage.get('value')) || 0;
      value += 1;
      await this.ctx.storage.put('value', value);
      return value;
    }

}
from workers import DurableObject

class Counter(DurableObject):
  def __init__(self, ctx, env):
    super().__init__(ctx, env)

  async def increment(self):
    value = (await self.ctx.storage.get('value')) or 0
    value += 1
    await self.ctx.storage.put('value', value)
    return value

JavaScript je jednovláknový a událostmi řízený programovací jazyk. To znamená, že běhová prostředí JavaScriptu standardně umožňují prokládání jednotlivých požadavků, což může vést k chybám souběžnosti. Běhové prostředí Durable Objects k jejich řešení používá kombinaci vstupní brány a výstupní brány, které zabraňují tomuto typu chyby souběžnosti při operacích s úložištěm. Více informací najdete v našem blogový příspěvek ↗.

SQL API

SqlStorage rozhraní zapouzdřuje metody, které upravují databázi SQLite vloženou do Durable Object. SqlStorage rozhraní je dostupné přes sql vlastnost z DurableObjectStorage třídu.

Například pomocí sql.exec() uživatel může vytvořit tabulku a vložit řádky.

import { DurableObject } from "cloudflare:workers";

export class MyDurableObject extends DurableObject {
  sql: SqlStorage;
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    this.sql = ctx.storage.sql;

    this.sql.exec(`
      CREATE TABLE IF NOT EXISTS artist(
        artistid    INTEGER PRIMARY KEY,
        artistname  TEXT
      );
      INSERT INTO artist (artistid, artistname) VALUES
        (123, 'Alice'),
        (456, 'Bob'),
        (789, 'Charlie');
    `);
  }
}
from workers import DurableObject

class MyDurableObject(DurableObject):
  def __init__(self, ctx, env):
    super().__init__(ctx, env)
    self.sql = ctx.storage.sql

    self.sql.exec("""
      CREATE TABLE IF NOT EXISTS artist(
        artistid    INTEGER PRIMARY KEY,
        artistname  TEXT
      );
      INSERT INTO artist (artistid, artistname) VALUES
        (123, 'Alice'),
        (456, 'Bob'),
        (789, 'Charlie');
    """)

Durable Objects podporují podmnožinu rozšíření SQLite pro doplňkovou funkčnost, včetně:

Viz zdrojový kód ↗ pro úplný seznam podporovaných funkcí.

exec

exec(query: string, ...bindings: any[]): SqlStorageCursor

Parametry

Vrací

Kurzor (SqlStorageCursor) pro iteraci nad výsledky řádků dotazu jako objekty. SqlStorageCursor je JavaScriptový Iterable ↗, která podporuje iteraci pomocí for (let row of cursor). SqlStorageCursor je také JavaScriptový Iterator ↗, která podporuje iteraci pomocí cursor.next().

SqlStorageCursor podporuje následující metody:

let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;");
let rawResult = cursor.raw().next();

if (!rawResult.done) {
  console.log(rawResult.value); // prints [ 123, 'Alice' ]
} else {
  // query returned zero results
}

console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }]
cursor = self.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;")
raw_result = cursor.raw().next()

if not raw_result.done:
  print(raw_result.value)  # prints [ 123, 'Alice' ]
else:
  # query returned zero results
  pass

print(cursor.toArray())  # prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }]

SqlStorageCursor má následující vlastnosti:

Příklady

SQL API příklady níže používají následující SQL schéma:

import { DurableObject } from "cloudflare:workers";

export class MyDurableObject extends DurableObject {
  sql: SqlStorage
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    this.sql = ctx.storage.sql;

    this.sql.exec(`CREATE TABLE IF NOT EXISTS artist(
      artistid    INTEGER PRIMARY KEY,
      artistname  TEXT
    );INSERT INTO artist (artistid, artistname) VALUES
      (123, 'Alice'),
      (456, 'Bob'),
      (789, 'Charlie');`
    );
  }
}

Iterace přes výsledky dotazu jako řádkové objekty:

  let cursor = this.sql.exec("SELECT * FROM artist;");

  for (let row of cursor) {
    // Iterate over row object and do something
  }

Převod výsledků dotazu na pole objektů řádků:

  // Return array of row objects: [{"artistid":123,"artistname":"Alice"},{"artistid":456,"artistname":"Bob"},{"artistid":789,"artistname":"Charlie"}]
  let resultsArray1 = this.sql.exec("SELECT * FROM artist;").toArray();
  // OR
  let resultsArray2 = Array.from(this.sql.exec("SELECT * FROM artist;"));
  // OR
  let resultsArray3 = [...this.sql.exec("SELECT * FROM artist;")]; // JavaScript spread syntax

Převod výsledků dotazu na pole polí hodnot řádků:

  // Returns [[123,"Alice"],[456,"Bob"],[789,"Charlie"]]
  let cursor = this.sql.exec("SELECT * FROM artist;");
  let resultsArray = cursor.raw().toArray();

  // Returns ["artistid","artistname"]
  let columnNameArray = this.sql.exec("SELECT * FROM artist;").columnNames.toArray();

Získání objektu prvního řádku z výsledků dotazu:

  // Returns {"artistid":123,"artistname":"Alice"}
  let firstRow = this.sql.exec("SELECT * FROM artist ORDER BY artistname DESC;").toArray()[0];

Zkontrolujte, zda výsledek dotazu obsahuje přesně jeden řádek:

  // returns error
  this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;").one();

  // returns { artistid: 123, artistname: 'Alice' }
  let oneRow = this.sql.exec("SELECT * FROM artist WHERE artistname = ?;", "Alice").one()

Chování vráceného kurzoru:

  let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;");
  let result = cursor.next();
  if (!result.done) {
    console.log(result.value); // prints { artistid: 123, artistname: 'Alice' }
  } else {
    // query returned zero results
  }

  let remainingRows = cursor.toArray();
  console.log(remainingRows); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }]

Vrácený kurzor a raw() iterátor procházejí stejné výsledky dotazu:

  let cursor = this.sql.exec("SELECT * FROM artist ORDER BY artistname ASC;");
  let result = cursor.raw().next();

  if (!result.done) {
    console.log(result.value); // prints [ 123, 'Alice' ]
  } else {
    // query returned zero results
  }

  console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{ artistid: 789, artistname: 'Charlie' }]

sql.exec().rowsRead():

  let cursor = this.sql.exec("SELECT * FROM artist;");
  cursor.next()
  console.log(cursor.rowsRead); // prints 1

  cursor.toArray(); // consumes remaining cursor
  console.log(cursor.rowsRead); // prints 3

databaseSize

databaseSize: number

Vrací

Aktuální velikost databáze SQLite v bajtech.

let size = ctx.storage.sql.databaseSize;
size = ctx.storage.sql.databaseSize

PITR (Point In Time Recovery) API

Pro Durable Objects podporované SQLite, jsou k dispozici následující metody API pro obnovu k určitému bodu v čase (point-in-time recovery, PITR), pomocí kterých lze vestavěnou databázi SQLite Durable Objectu obnovit na libovolný okamžik za posledních 30 dní. Tyto metody se vztahují na celý obsah databáze SQLite, včetně uložených SQL dat objektu i dat uložených pomocí key-value put() API. PITR API není v lokálním vývoji podporováno, protože trvalý log změn dat se lokálně neukládá.

PITR API reprezentuje body v čase pomocí takzvaných „bookmarks“. Bookmark je většinou alfanumerický řetězec, například 0000007b-0000b26e-00001538-0c3e87bb37b3db5cc52eedb93cd3b96b. Bookmarky jsou navržené tak, aby byly lexikálně porovnatelné: při běžném porovnání řetězců je bookmark reprezentující dřívější časový okamžik menší než bookmark reprezentující okamžik pozdější.

getCurrentBookmark

ctx.storage.getCurrentBookmark(): Promise<string>

getBookmarkForTime

ctx.storage.getBookmarkForTime(timestamp: number | Date): Promise<string>

onNextSessionRestoreBookmark

ctx.storage.onNextSessionRestoreBookmark(bookmark: string): Promise<string>

Tato metoda vrací speciální bookmark představující okamžik těsně před provedením obnovy (i když je tento okamžik technicky vzato stále v budoucnosti). Po dokončení obnovy ho lze vrátit zpět provedením druhé obnovy k tomuto bookmarku.

const DAY_MS = 24*60*60*1000;
// restore to 2 days ago
let bookmark = ctx.storage.getBookmarkForTime(Date.now() - 2 * DAYS_MS);
ctx.storage.onNextSessionRestoreBookmark(bookmark);
from datetime import datetime, timedelta

now = datetime.now()
# restore to 2 days ago
bookmark = ctx.storage.getBookmarkForTime(now - timedelta(days=2))
ctx.storage.onNextSessionRestoreBookmark(bookmark)

Synchronní KV API

get

put

delete

list

Podporované možnosti

Asynchronní KV API

získat

Podporované možnosti

put

smazat

Podporované možnosti

seznam

Podporované možnosti

Alarmy

getAlarm

Podporované možnosti

setAlarm

deleteAlarm

Podporované možnosti

Ostatní

deleteAll

transactionSync

transaction

sync

Vlastnosti úložiště

sql

sql je vlastnost pouze pro čtení typu DurableObjectStorage zapouzdřující SQL API.