Mit Cloud Run-Funktionen (2. Generation) erweitern

Mit Cloud Run-Funktionen können Sie Code bereitstellen, um Ereignisse zu verarbeiten, die durch Änderungen in Ihrer Firestore-Datenbank ausgelöst werden. So können Sie serverseitige Funktionen hinzufügen, ohne eigene Server betreiben zu müssen.

Firestore mit Cloud Run-Funktionen (2. Generation) erweitern

Cloud Run-Funktionen (2. Generation) unterstützen die folgenden Firestore-Ereignistrigger, mit denen Sie Handler erstellen können, die mit Firestore-Ereignissen verknüpft sind:

Ereignistyp Trigger
google.cloud.firestore.document.v1.created Wird ausgelöst, wenn ein Dokument zum ersten Mal beschrieben wird.
google.cloud.firestore.document.v1.updated Wird ausgelöst, wenn ein Dokument bereits existiert und sich ein Wert geändert hat.
google.cloud.firestore.document.v1.deleted Wird ausgelöst, wenn ein Dokument gelöscht wird.
google.cloud.firestore.document.v1.written Wird ausgelöst, wenn created, updated oder deleted ausgelöst wird.
google.cloud.firestore.document.v1.created.withAuthContext Entspricht created, fügt aber Authentifizierungsinformationen hinzu.
google.cloud.firestore.document.v1.updated.withAuthContext Entspricht updated, fügt aber Authentifizierungsinformationen hinzu.
google.cloud.firestore.document.v1.deleted.withAuthContext Entspricht deleted, fügt aber Authentifizierungsinformationen hinzu.
google.cloud.firestore.document.v1.written.withAuthContext Entspricht written, fügt aber Authentifizierungsinformationen hinzu.

Firestore-Ereignisse werden nur bei Dokumentänderungen ausgelöst. Die Aktualisierung eines Firestore-Dokuments, bei der die Daten unverändert bleiben (also ohne Schreibvorgang), generiert kein Aktualisierungs- oder Schreibereignis. Es ist nicht möglich, bestimmten Feldern Ereignisse hinzuzufügen.

Authentifizierungskontext in das Ereignis einschließen

Wenn Sie zusätzliche Authentifizierungsinformationen zum Ereignis einbeziehen möchten, verwenden Sie einen Ereignistrigger mit der Erweiterung withAuthContext. Diese Erweiterung fügt zusätzliche Informationen zum Hauptkonto hinzu, das das Ereignis ausgelöst hat. Zusätzlich zu den Informationen, die im Basisereignis zurückgegeben werden, werden die Attribute authtype und authid hinzugefügt. Weitere Informationen zu Attributwerten finden Sie in der Referenz authcontext.

Firestore-ausgelöste Funktion schreiben

Wenn Sie eine Funktion schreiben möchten, die auf Firestore-Ereignisse reagiert, müssen Sie während der Bereitstellung Folgendes angeben:

  • einen Triggerereignistyp
  • einen Triggerereignisfilter, um die mit der Funktion verknüpften Dokumente auszuwählen
  • den auszuführenden Funktionscode

Triggerereignisfilter

Wenn Sie einen Ereignisfilter angeben, können Sie entweder eine genaue Dokumentübereinstimmung oder ein Pfadmuster angeben. Verwenden Sie ein Pfadmuster, um mehrere Dokumente mit Platzhaltern wie * oder ** abzugleichen.

Sie können beispielsweise auf Änderungen an folgenden Dokumenten reagieren:

users/marie

Verwenden Sie Platzhalter, * oder **, um auf Änderungen in Dokumenten zu reagieren, die einem Muster entsprechen. Ein Platzhalter * entspricht einem einzelnen Segment und ein Platzhalter mit mehreren Segmenten ** entspricht null oder mehr Segmenten im Muster.

Für Übereinstimmungen mit einzelnen Segmenten (*) können Sie auch eine benannte Erfassungsgruppe verwenden. Beispiel: users/{userId}

Beispiel:

Muster Beschreibung
/users/* oder /users/{userId} Entspricht allen Dokumenten in der Sammlung /users. Stimmt nicht mit Dokumenten in Untersammlungen wie /users/marie/messages/33e2IxYBD9enzS50SJ68 überein
/users/** Führt zu Übereinstimmung mit allen Dokumenten in der Sammlung /users und Dokumenten in Untersammlungen wie /users/marie/messages/33e2IxYBD9enzS50SJ68

Weitere Informationen zu Pfadmustern finden Sie unter Eventarc-Pfadmuster.

Ihr Trigger muss immer auf ein Dokument verweisen, auch wenn Sie einen Platzhalter verwenden. Beispiel: users/{userId=*}/{messageCollectionId=*} ist ungültig, da {messageCollectionId=*} eine Sammlung ist. users/{userId=*}/{messageCollectionId}/{messageId=*} ist jedoch gültig, da {messageId=*} immer auf ein Dokument verweist.

Beispielfunktionen

Im folgenden Beispiel wird gezeigt, wie Firestore-Ereignisse empfangen werden. Informationen zu den Dokumentdaten, die mit einem Ereignis verknüpft sind, finden Sie in den Feldern value und old_value.

  • value: Ein Document-Objekt, das einen Snapshot des Dokuments nach der Operation enthält. Dieses Feld wird für Löschereignisse nicht ausgefüllt.
  • old_value: Ein Document-Objekt, das einen Snapshot des Dokuments vor dem Vorgang enthält. Dieses Feld wird nur für Aktualisierungs- und Löschereignisse ausgefüllt.

Go

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.


// Package hellofirestore contains a Cloud Event Function triggered by a Cloud Firestore event.
package hellofirestore

import (
	"context"
	"fmt"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

func init() {
	functions.CloudEvent("helloFirestore", HelloFirestore)
}

// HelloFirestore is triggered by a change to a Firestore document.
func HelloFirestore(ctx context.Context, event event.Event) error {
	var data firestoredata.DocumentEventData
	if err := proto.Unmarshal(event.Data(), &data); err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	fmt.Printf("Function triggered by change to: %v\n", event.Source())
	fmt.Printf("Old value: %+v\n", data.GetOldValue())
	fmt.Printf("New value: %+v\n", data.GetValue())
	return nil
}

Java

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.logging.Logger;

public class FirebaseFirestore implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestore.class.getName());

  @Override
  public void accept(CloudEvent event) throws InvalidProtocolBufferException {
    DocumentEventData firestorEventData = DocumentEventData.parseFrom(event.getData().toBytes());

    logger.info("Function triggered by event on: " + event.getSource());
    logger.info("Event type: " + event.getType());

    logger.info("Old value:");
    logger.info(firestorEventData.getOldValue().toString());

    logger.info("New value:");
    logger.info(firestorEventData.getValue().toString());
  }
}

Node.js

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

Verwenden Sie protobufjs, um die Ereignisdaten zu decodieren. Fügen Sie google.events.cloud.firestore.v1 data.proto in Ihre Quelle ein.
/**
 * Cloud Event Function triggered by a change to a Firestore document.
 */
const functions = require('@google-cloud/functions-framework');
const protobuf = require('protobufjs');

functions.cloudEvent('helloFirestore', async cloudEvent => {
  console.log(`Function triggered by event on: ${cloudEvent.source}`);
  console.log(`Event type: ${cloudEvent.type}`);

  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  console.log('\nOld value:');
  console.log(JSON.stringify(firestoreReceived.oldValue, null, 2));

  console.log('\nNew value:');
  console.log(JSON.stringify(firestoreReceived.value, null, 2));
});

Python

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

from cloudevents.http import CloudEvent
import functions_framework
from google.events.cloud import firestore


@functions_framework.cloud_event
def hello_firestore(cloud_event: CloudEvent) -> None:
    """Triggers by a change to a Firestore document.

    Args:
        cloud_event: cloud event with information on the firestore event trigger
    """
    firestore_payload = firestore.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    print(f"Function triggered by change to: {cloud_event['source']}")

    print("\nOld value:")
    print(firestore_payload.old_value)

    print("\nNew value:")
    print(firestore_payload.value)

C#

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Cloud.Firestore.V1;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace FirebaseFirestore;

public class Function : ICloudEventFunction<DocumentEventData>
{
    private readonly ILogger _logger;

    public Function(ILogger<Function> logger) =>
        _logger = logger;

    public Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Function triggered by event on {subject}", cloudEvent.Subject);
        _logger.LogInformation("Event type: {type}", cloudEvent.Type);
        MaybeLogDocument("Old value", data.OldValue);
        MaybeLogDocument("New value", data.Value);

        // In this example, we don't need to perform any asynchronous operations, so the
        // method doesn't need to be declared async.
        return Task.CompletedTask;
    }

    /// <summary>
    /// Logs the names and values of the fields in a document in a very simplistic way.
    /// </summary>
    private void MaybeLogDocument(string message, Document document)
    {
        if (document is null)
        {
            return;
        }

        // ConvertFields converts the Firestore representation into a .NET-friendly
        // representation.
        IReadOnlyDictionary<string, object> fields = document.ConvertFields();
        var fieldNamesAndTypes = fields
            .OrderBy(pair => pair.Key)
            .Select(pair => $"{pair.Key}: {pair.Value}");
        _logger.LogInformation(message + ": {fields}", string.Join(", ", fieldNamesAndTypes));
    }
}

In den folgenden Beispielen werden Strings, die dem Feld original eines betroffenen Dokuments hinzugefügt wurden, in Großbuchstaben umgewandelt und der neue Wert in dasselbe Dokument geschrieben:

Go

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.


// Package upper contains a Firestore Cloud Function.
package upper

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"

	"cloud.google.com/go/firestore"
	firebase "firebase.google.com/go/v4"
	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

// set the GOOGLE_CLOUD_PROJECT environment variable when deploying.
var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")

// client is a Firestore client, reused between function invocations.
var client *firestore.Client

func init() {
	// Use the application default credentials.
	conf := &firebase.Config{ProjectID: projectID}

	// Use context.Background() because the app/client should persist across
	// invocations.
	ctx := context.Background()

	app, err := firebase.NewApp(ctx, conf)
	if err != nil {
		log.Fatalf("firebase.NewApp: %v", err)
	}

	client, err = app.Firestore(ctx)
	if err != nil {
		log.Fatalf("app.Firestore: %v", err)
	}

	// Register cloud event function
	functions.CloudEvent("MakeUpperCase", MakeUpperCase)
}

// MakeUpperCase is triggered by a change to a Firestore document. It updates
// the `original` value of the document to upper case.
func MakeUpperCase(ctx context.Context, e event.Event) error {
	var data firestoredata.DocumentEventData
	if err := proto.Unmarshal(e.Data(), &data); err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	if data.GetValue() == nil {
		return errors.New("Invalid message: 'Value' not present")
	}

	fullPath := strings.Split(data.GetValue().GetName(), "/documents/")[1]
	pathParts := strings.Split(fullPath, "/")
	collection := pathParts[0]
	doc := strings.Join(pathParts[1:], "/")

	var originalStringValue string
	if v, ok := data.GetValue().GetFields()["original"]; ok {
		originalStringValue = v.GetStringValue()
	} else {
		return errors.New("Document did not contain field \"original\"")
	}

	newValue := strings.ToUpper(originalStringValue)
	if originalStringValue == newValue {
		log.Printf("%q is already upper case: skipping", originalStringValue)
		return nil
	}
	log.Printf("Replacing value: %q -> %q", originalStringValue, newValue)

	newDocumentEntry := map[string]string{"original": newValue}
	_, err := client.Collection(collection).Doc(doc).Set(ctx, newDocumentEntry)
	if err != nil {
		return fmt.Errorf("Set: %w", err)
	}
	return nil
}

Java

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.firestore.SetOptions;
import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.events.cloud.firestore.v1.Value;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;

public class FirebaseFirestoreReactive implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestoreReactive.class.getName());
  private final Firestore firestore;

  private static final String FIELD_KEY = "original";
  private static final String APPLICATION_PROTOBUF = "application/protobuf";

  public FirebaseFirestoreReactive() {
    this(FirestoreOptions.getDefaultInstance().getService());
  }

  public FirebaseFirestoreReactive(Firestore firestore) {
    this.firestore = firestore;
  }

  @Override
  public void accept(CloudEvent event)
      throws InvalidProtocolBufferException, InterruptedException, ExecutionException {
    if (event.getData() == null) {
      logger.warning("No data found in event!");
      return;
    }

    if (!event.getDataContentType().equals(APPLICATION_PROTOBUF)) {
      logger.warning(String.format("Found unexpected content type %s, expected %s",
          event.getDataContentType(),
          APPLICATION_PROTOBUF));
      return;
    }

    DocumentEventData firestoreEventData = DocumentEventData
        .parseFrom(event.getData().toBytes());

    // Get the fields from the post-operation document snapshot
    // https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
    Map<String, Value> fields = firestoreEventData.getValue().getFieldsMap();
    if (!fields.containsKey(FIELD_KEY)) {
      logger.warning("Document does not contain original field");
      return;
    }
    String currValue = fields.get(FIELD_KEY).getStringValue();
    String newValue = currValue.toUpperCase();

    if (currValue.equals(newValue)) {
      logger.info("Value is already upper-case");
      return;
    }

    // Retrieve the document name from the resource path:
    // projects/{project_id}/databases/{database_id}/documents/{document_path}
    String affectedDoc = firestoreEventData.getValue()
        .getName()
        .split("/documents/")[1]
        .replace("\"", "");

    logger.info(String.format("Replacing values: %s --> %s", currValue, newValue));

    // Wait for the async call to complete
    this.firestore
        .document(affectedDoc)
        .set(Map.of(FIELD_KEY, newValue), SetOptions.merge())
        .get();
  }
}

Node.js

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

Verwenden Sie protobufjs, um die Ereignisdaten zu decodieren. Fügen Sie google.events.cloud.firestore.v1 data.proto in Ihre Quelle ein.
const functions = require('@google-cloud/functions-framework');
const Firestore = require('@google-cloud/firestore');
const protobuf = require('protobufjs');

const firestore = new Firestore({
  projectId: process.env.GOOGLE_CLOUD_PROJECT,
});

// Converts strings added to /messages/{pushId}/original to uppercase
functions.cloudEvent('makeUpperCase', async cloudEvent => {
  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  const resource = firestoreReceived.value.name;
  const affectedDoc = firestore.doc(resource.split('/documents/')[1]);

  const curValue = firestoreReceived.value.fields.original.stringValue;
  const newValue = curValue.toUpperCase();

  if (curValue === newValue) {
    // Value is already upper-case
    // Don't perform a(nother) write to avoid infinite loops
    console.log('Value is already upper-case.');
    return;
  }

  console.log(`Replacing value: ${curValue} --> ${newValue}`);
  affectedDoc.set({
    original: newValue,
  });
});

Python

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

from cloudevents.http import CloudEvent
import functions_framework
from google.cloud import firestore
from google.events.cloud import firestore as firestoredata

client = firestore.Client()


# Converts strings added to /messages/{pushId}/original to uppercase
@functions_framework.cloud_event
def make_upper_case(cloud_event: CloudEvent) -> None:
    firestore_payload = firestoredata.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    path_parts = firestore_payload.value.name.split("/")
    separator_idx = path_parts.index("documents")
    collection_path = path_parts[separator_idx + 1]
    document_path = "/".join(path_parts[(separator_idx + 2) :])

    print(f"Collection path: {collection_path}")
    print(f"Document path: {document_path}")

    affected_doc = client.collection(collection_path).document(document_path)

    cur_value = firestore_payload.value.fields["original"].string_value
    new_value = cur_value.upper()

    if cur_value != new_value:
        print(f"Replacing value: {cur_value} --> {new_value}")
        affected_doc.set({"original": new_value})
    else:
        # Value is already upper-case
        # Don't perform a second write (which can trigger an infinite loop)
        print("Value is already upper-case.")

C#

Richten Sie die Standardanmeldedaten für Anwendungen ein, um sich bei Firestore zu authentifizieren. Weitere Informationen finden Sie unter Authentifizierung für eine lokale Entwicklungsumgebung einrichten.

using CloudNative.CloudEvents;
using Google.Cloud.Firestore;
using Google.Cloud.Functions.Framework;
using Google.Cloud.Functions.Hosting;
using Google.Events.Protobuf.Cloud.Firestore.V1;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace FirestoreReactive;

public class Startup : FunctionsStartup
{
    public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) =>
        services.AddSingleton(FirestoreDb.Create());
}

// Register the startup class to provide the Firestore dependency.
[FunctionsStartup(typeof(Startup))]
public class Function : ICloudEventFunction<DocumentEventData>
{
    private readonly ILogger _logger;
    private readonly FirestoreDb _firestoreDb;

    public Function(ILogger<Function> logger, FirestoreDb firestoreDb) =>
        (_logger, _firestoreDb) = (logger, firestoreDb);

    public async Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken)
    {
        // Get the recently-written value. This expression will result in a null value
        // if any of the following is true:
        // - The event doesn't contain a "new" document
        // - The value doesn't contain a field called "original"
        // - The "original" field isn't a string
        string currentValue = data.Value?.ConvertFields().GetValueOrDefault("original") as string;
        if (currentValue is null)
        {
            _logger.LogWarning($"Event did not contain a suitable document");
            return;
        }

        string newValue = currentValue.ToUpperInvariant();
        if (newValue == currentValue)
        {
            _logger.LogInformation("Value is already upper-cased; no replacement necessary");
            return;
        }

        // The CloudEvent subject is "documents/x/y/...".
        // The Firestore SDK FirestoreDb.Document method expects a reference relative to
        // "documents" (so just the "x/y/..." part). This may be simplified over time.
        if (cloudEvent.Subject is null || !cloudEvent.Subject.StartsWith("documents/"))
        {
            _logger.LogWarning("CloudEvent subject is not a document reference.");
            return;
        }
        string documentPath = cloudEvent.Subject.Substring("documents/".Length);

        _logger.LogInformation("Replacing '{current}' with '{new}' in '{path}'", currentValue, newValue, documentPath);
        await _firestoreDb.Document(documentPath).UpdateAsync("original", newValue, cancellationToken: cancellationToken);
    }
}

Proto-Abhängigkeiten in die Quelle einbinden

Sie müssen die Datei Firestore data.proto in das Quellverzeichnis Ihrer Funktion aufnehmen. Diese Datei importiert die folgenden Protodateien, die Sie auch in Ihr Quellverzeichnis aufnehmen müssen:

Verwenden Sie für die Abhängigkeiten dieselbe Verzeichnisstruktur. Platzieren Sie beispielsweise struct.proto in google/protobuf.

Diese Dateien sind erforderlich, um Ereignisdaten zu decodieren. Wenn Ihre Funktionsquelle diese Dateien nicht enthält, wird beim Ausführen ein Fehler zurückgegeben.

Ereignisattribute

Jedes Ereignis enthält Datenattribute mit Informationen zum Ereignis, z. B. zum Zeitpunkt des Auslösens. Firestore fügt zusätzliche Daten zur Datenbank und zum Dokument hinzu, die am Ereignis beteiligt sind. So greifen Sie auf diese Attribute zu:

Java
logger.info("Function triggered by event on: " + event.getSource());
logger.info("Event type: " + event.getType());
logger.info("Event time " + event.getTime());
logger.info("Event project: " + event.getExtension("project"));
logger.info("Event location: " + event.getExtension("location"));
logger.info("Database name: " + event.getExtension("database"));
logger.info("Database document: " + event.getExtension("document"));
// For withAuthContext events
logger.info("Auth information: " + event.getExtension("authid"));
logger.info("Auth information: " + event.getExtension("authtype"));
Node.js
console.log(`Function triggered by event on: ${cloudEvent.source}`);
console.log(`Event type: ${cloudEvent.type}`);
console.log(`Event time: ${cloudEvent.time}`);
console.log(`Event project: ${cloudEvent.project}`);
console.log(`Event location: ${cloudEvent.location}`);
console.log(`Database name: ${cloudEvent.database}`);
console.log(`Document name: ${cloudEvent.document}`);
// For withAuthContext events
console.log(`Auth information: ${cloudEvent.authid}`);
console.log(`Auth information: ${cloudEvent.authtype}`);
Python
print(f"Function triggered by change to: {cloud_event['source']}")
print(f"Event type: {cloud_event['type']}")
print(f"Event time: {cloud_event['time']}")
print(f"Event project: {cloud_event['project']}")
print(f"Location: {cloud_event['location']}")
print(f"Database name: {cloud_event['database']}")
print(f"Document: {cloud_event['document']}")
// For withAuthContext events
print(f"Auth information: {cloud_event['authid']}")
print(f"Auth information: {cloud_event['authtype']}")

Funktion bereitstellen

Nutzer, die Cloud Run Functions bereitstellen, müssen die IAM-Rolle Cloud Run-Entwickler oder eine Rolle mit denselben Berechtigungen haben. Siehe auch Zusätzliche Konfiguration für die Bereitstellung.

Sie können eine Funktion entweder mit der gcloud CLI oder der Google Cloud Console bereitstellen. Im folgenden Beispiel wird die Bereitstellung mit der gcloud CLI veranschaulicht. Weitere Informationen zur Bereitstellung über die Google Cloud Console finden Sie unter Cloud Run Functions bereitstellen.

gcloud

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. Verwenden Sie den Befehl gcloud functions deploy, um eine Funktion bereitzustellen:

    gcloud functions deploy YOUR_FUNCTION_NAME \
    --gen2 \
    --region=FUNCTION_LOCATION \
    --trigger-location=TRIGGER_LOCATION \
    --runtime=YOUR_RUNTIME \
    --source=YOUR_SOURCE_LOCATION \
    --entry-point=YOUR_CODE_ENTRYPOINT \
    --trigger-event-filters="type=EVENT_FILTER_TYPE" \
    --trigger-event-filters="database=DATABASE" \
    --trigger-event-filters-path-pattern="document=DOCUMENT" \
    

    Das erste Argument, YOUR_FUNCTION_NAME, ist ein Name für Ihre bereitgestellte Funktion. Der Name muss mit einem Buchstaben beginnen, gefolgt von bis zu 62 Buchstaben, Ziffern, Bindestrichen oder Unterstrichen. Das letzte Zeichen muss ein Buchstabe oder eine Ziffer sein.

    • Das Flag --gen2 gibt an, dass Sie die Bereitstellung in Cloud Run Functions (2. Generation) vornehmen möchten. Wenn Sie dieses Flag weglassen, erfolgt die Bereitstellung in Cloud Run Functions (1. Generation).

    • Das Flag --region gibt die Region an, in der die Funktion bereitgestellt werden soll.

      Legen Sie für eine maximale Nähe eine Region in der Nähe Ihrer Firestore-Datenbank fest. Wenn sich Ihre Firestore-Datenbank an einem multiregionalen Standort befindet, legen Sie für Datenbankspeicherorte in nam5 den Wert us-central1 und für eur3 den Wert europe-west4 fest. Legen Sie für regionale Firestore-Standorte dieselbe Region fest.

    • Das Flag --trigger-location gibt den Speicherort des Trigger an. Sie müssen dieses Flag auf den Speicherort Ihrer Firestore-Datenbank setzen.

    • Das Flag --runtime gibt an, welche Sprachlaufzeit die Funktion verwendet. Cloud Run Functions unterstützt mehrere Laufzeiten. Weitere Informationen finden Sie unter Laufzeiten.

    • Das Flag --source gibt den Speicherort des Quellcodes der Funktion an. Weitere Informationen finden Sie hier:

    • Das Flag --entry-point gibt den Einstiegspunkt für die Funktion in Ihrem Quellcode an. Dies ist der Code, der beim Ausführen der Funktion ausgeführt wird. Der Wert dieses Flags muss ein Funktionsname oder ein voll qualifizierter Klassenname sein, der in Ihrem Quellcode vorhanden ist. Weitere Informationen finden Sie unter Funktionseinstiegspunkt.

    • EVENT_FILTER_TYPE: Firestore unterstützt die folgenden Ereignistypen.

      • google.cloud.firestore.document.v1.created: Dieses Ereignis wird gesendet, wenn zum ersten Mal in ein Dokument geschrieben wird.
      • google.cloud.firestore.document.v1.updated: Dieses Ereignis wird gesendet, wenn ein Dokument bereits vorhanden ist und sich ein Wert geändert hat.
      • google.cloud.firestore.document.v1.deleted: Dieses Ereignis wird gesendet, wenn ein Dokument gelöscht wird.
      • google.cloud.firestore.document.v1.written: Dieses Ereignis wird gesendet, wenn ein Dokument erstellt, aktualisiert oder gelöscht wird.
      • google.cloud.firestore.document.v1.created.withAuthContext: Dieses Ereignis wird gesendet, wenn zum ersten Mal in ein Dokument geschrieben wird und das Ereignis zusätzliche Authentifizierungsinformationen enthält.
      • google.cloud.firestore.document.v1.updated.withAuthContext: Dieses Ereignis wird gesendet, wenn ein Dokument bereits vorhanden ist und sich ein Wert geändert hat. Enthält zusätzliche Authentifizierungsinformationen
      • google.cloud.firestore.document.v1.deleted.withAuthContext: Dieses Ereignis wird gesendet, wenn ein Dokument gelöscht wird. Enthält zusätzliche Authentifizierungsinformationen
      • google.cloud.firestore.document.v1.written.withAuthContext: Dieses Ereignis wird gesendet, wenn ein Dokument erstellt, aktualisiert oder gelöscht wird. Enthält zusätzliche Authentifizierungsinformationen
    • DATABASE: Firestore-Datenbank. Verwenden Sie (default) als Standarddatenbanknamen.

    • DOCUMENT: der Datenbankpfad, der Ereignisse auslöst, wenn Daten erstellt, aktualisiert oder gelöscht werden. Als Operator kommen die folgenden Zeichen infrage:

      • Gleich. Beispiel: --trigger-event-filters=document='users/marie'
      • Pfadmuster. Beispiel: --trigger-event-filters-path-pattern=document='users/*'. Weitere Informationen finden Sie unter Informationen zu Pfadmustern.

    Sie können optional weitere Konfigurations-, Netzwerk- und Sicherheitsoptionen angeben, wenn Sie eine Funktion bereitstellen.

    Eine vollständige Referenz zum Bereitstellungsbefehl und seinen Flags finden Sie in der Dokumentation zu gcloud functions deploy.

Beispielimplementierungen

In den folgenden Beispielen werden Bereitstellungen mit der Google Cloud CLI veranschaulicht.

Funktion für eine Datenbank in der Region us-west2 bereitstellen:

gcloud functions deploy gcfv2-trigger-firestore-node \
--gen2 \
--region=us-west2 \
--trigger-location=us-west2 \
--runtime=nodejs18 \
--source=gs://CLOUD_STORAGE_BUCKET/firestoreEventFunction.zip \
--entry-point=makeUpperCase \
--trigger-event-filters=type=google.cloud.firestore.document.v1.written \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern=document='messages/{pushId}'

So stellen Sie eine Funktion für eine Datenbank in der Multi-Region nam5 bereit:

gcloud functions deploy gcfv2-trigger-firestore-python \
--gen2 \
--region=us-central1 \
--trigger-location=nam5 \
--runtime=python311 \
--source=gs://CLOUD_STORAGE_BUCKET/firestoreEventFunction.zip \
--entry-point=make_upper_case \
--trigger-event-filters=type=google.cloud.firestore.document.v1.written.withAuthContext \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern=document='messages/{pushId}'

Beschränkungen

Beachten Sie die folgenden Einschränkungen für Firestore-Trigger für Cloud Run Functions:

  • Cloud Run Functions (1. Generation) erfordert eine vorhandene „(default)“-Datenbank im nativen Firestore-Modus. Es unterstützt keine benannten Firestore-Datenbanken und keinen Datastore-Modus. Verwenden Sie in solchen Fällen Cloud Run Functions (2. Generation), um Ereignisse zu konfigurieren.
  • Die Reihenfolge ist nicht garantiert. Schnelle Änderungen können Funktionsaufrufe in einer unvorhergesehenen Reihenfolge auslösen.
  • Ereignisse werden mindestens einmal übergeben. Ein einzelnes Ereignis kann aber zu mehreren Funktionsaufrufen führen. Vermeiden Sie die Abhängigkeit von genau einmal vorkommenden Verfahren und schreiben Sie idempotente Funktionen.
  • Firestore im Datastore-Modus erfordert Cloud Run Functions (2. Generation). Cloud Run Functions (1. Generation) unterstützt den Datastore-Modus nicht.
  • Ein Trigger ist mit einer einzelnen Datenbank verknüpft. Sie können keinen Trigger erstellen, der mit mehreren Datenbanken übereinstimmt.
  • Wenn Sie eine Datenbank löschen, werden nicht automatisch die Trigger für diese Datenbank gelöscht. Der Trigger sendet keine Ereignisse mehr, bleibt aber bestehen, bis Sie ihn löschen.
  • Wenn ein übereinstimmendes Ereignis die maximale Anfragegröße überschreitet, wird es möglicherweise nicht an Cloud Run Functions der 1. Generation gesendet.
    • Ereignisse, die aufgrund der Anfragegröße nicht gesendet wurden, werden in Plattform-Logs protokolliert und in die Lognutzung für das Projekt einbezogen.
    • Sie finden diese Logs im Log-Explorer mit der Meldung "Ereignis kann nicht an die Cloud Functions-Funktion gesendet werden, da die Größe das Limit für die 1. Generation überschreitet..." mit dem Schweregrad error. Den Funktionsnamen finden Sie im Feld functionName. Wenn das Feld receiveTimestamp noch eine Stunde gültig ist, können Sie den tatsächlichen Ereignisinhalt ableiten, indem Sie das betreffende Dokument mit einem Snapshot vor und nach dem Zeitstempel lesen.
    • So können Sie das vermeiden:
      • Migrieren und Upgrade auf Cloud Run Functions (2. Generation)
      • Dokument verkleinern
      • Die betreffenden Cloud Run Functions-Funktionen löschen
    • Sie können die Protokollierung selbst mithilfe von Ausschlüssen deaktivieren. Die betreffenden Ereignisse werden jedoch weiterhin nicht gesendet.

Eventarc- und Firestore-Standorte

Eventarc unterstützt keine Mehrfachregionen für Firestore-Ereignistrigger. Sie können jedoch weiterhin Trigger für Firestore-Datenbanken an Standorten mit mehreren Regionen erstellen. In Eventarc werden Firestore-Standorte mit mehreren Regionen den folgenden Eventarc-Regionen zugeordnet:

Firestore multiregional Eventarc-Region
nam5 us-central1
eur3 europe-west4

Unterschiede zwischen Cloud Run-Funktionen der 2. Generation und der 1. Generation

Cloud Run-Funktionen (2. Generation) verwenden Eventarc-Ereignisse für alle Laufzeiten. Bisher wurden bei Cloud Run-Funktionen (1. Generation) nur für einige Laufzeiten Eventarc-Ereignisse verwendet. Eventarc-Ereignisse unterscheiden sich von Cloud Run-Funktionen (1. Generation) durch die folgenden Unterschiede:

  • Die Firestore-Trigger für Eventarc unterstützen neben Cloud Run-Funktionen auch zusätzliche Ziele. Sie können CloudEvents an eine Reihe von Zielen weiterleiten, darunter Cloud Run, GKE und Workflows.

  • Firestore-Trigger für Eventarc rufen die Triggerdefinition zu Beginn eines Datenbankschreibvorgangs ab und entscheiden anhand dieser Definition, ob Firestore ein Ereignis senden soll. Der Schreibvorgang berücksichtigt keine Änderungen an der Triggerdefinition, die während der Ausführung auftreten können.

    Bei Cloud Run Functions (1. Generation) wird die Triggerdefinition während der Auswertung des Datenbankschreibens abgerufen. Änderungen am Trigger während der Auswertung können sich darauf auswirken, ob Firestore ein Ereignis ausgibt oder nicht.

Weitere Informationen finden Sie im Versionsvergleich von Cloud Run Functions.