previous

package
v1.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 14, 2026 License: MIT Imports: 11 Imported by: 0

README

previous

Package previous stores a bounded event list per entity and returns a value from the most recent matching previous event. It is useful for features such as "previous amount for this user" or "previous amount from a different merchant".

The package uses only Operate for reads and writes, and Delete for cleanup. A *client.Client satisfies those small interfaces.

Strengths

  • Keeps previous-event feature logic in a reusable SDK package with small client interfaces, so callers can test against narrow mocks and avoid private server dependencies.
  • Stores a bounded list per derived entity key. MaxCount limits retained entries, and the default of 100 prevents unbounded list growth when callers do not choose a value.
  • Uses deterministic key hashing for storage keys. The key is derived from Name, Ref, the reference value, and FilterBy values, with FilterBy order normalized before hashing.
  • Supports both "read before write" and "write before read" flows through IncludeCurrent, while still skipping entries with the current EventID.
  • Preserves the retrieved Go value through src.Value(req.Retrieve), which lets callers store numeric, string, or other MessagePack-supported values without a string-only conversion step.

Weaknesses / Tradeoffs

  • Reads scan the stored list from newest to oldest in the client after one Operate read. Very large MaxCount values increase decode and scan work, so keep the bound close to the feature's real lookback need.
  • Writes may require a second Operate call when trimming is needed after an append. That keeps storage bounded, but trimming adds latency on overflow writes.
  • The storage key uses SHA-1 for deterministic, server-compatible key derivation only. Do not treat it as a security boundary or collision-resistant identifier for untrusted protocols.
  • queries.MapSource returns empty strings for missing or non-string Ref, Exclude, and FilterBy values. Missing fields can therefore group records together unless the caller validates input first.
  • ListFlagAddUnique|ListFlagNoFail makes repeated identical entries idempotent, but uniqueness is based on the full list entry. Different event IDs with the same retrieve value are still separate entries.

Execute

Execute reads and writes in one helper call:

  • With IncludeCurrent: false, it reads the previous value first and then writes the current event.
  • With IncludeCurrent: true, it writes the current event first and then reads; reads still skip entries with the current EventID.

TTL, Ref, and Retrieve are required. MaxCount defaults to 100 when it is zero or negative.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/FrogoAI/fdb-client/pkg/client"
	"github.com/FrogoAI/fdb-client/pkg/queries"
	"github.com/FrogoAI/fdb-client/pkg/queries/previous"
)

func main() {
	c, err := client.New("localhost:3000")
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	src := queries.NewMapSource(map[string]any{
		"standard.user_id":     "user-42",
		"standard.merchant_id": "merchant-7",
		"amount":               89.95,
		"event_id":             "evt-1234",
	})

	result, err := previous.Execute(context.Background(), c, previous.Request{
		Name:           "prev_amount_diff_merchant",
		Namespace:      "scoring",
		Ref:            "standard.user_id",
		Retrieve:       "amount",
		Exclude:        "standard.merchant_id",
		EventID:        src.String("event_id"),
		MaxCount:       50,
		TTL:            24 * time.Hour,
		IncludeCurrent: false,
	}, src)
	if err != nil {
		log.Fatal(err)
	}

	if result.Found {
		fmt.Printf("previous amount: %v\n", result.Value)
	}
}

Ref identifies the entity by reading src.String(req.Ref). Retrieve is read with src.Value, so the returned Result.Value keeps the stored Go value type. When Exclude is set, the read returns the newest non-current entry whose exclude value differs from the current event's exclude value.

Read Only

Use Read when another path writes events and the caller only needs the stored previous value.

result, err := previous.Read(ctx, c, previous.Request{
	Name:      "prev_amount",
	Namespace: "scoring",
	Ref:       "standard.user_id",
	Retrieve:  "amount",
	EventID:   "evt-1234",
	TTL:       24 * time.Hour,
}, src)
if err != nil {
	return err
}
if result.Found {
	fmt.Println(result.Value)
}

Storage and Cleanup

Records are stored in set previous. The deterministic record key is derived from Name, Ref, src.String(Ref), and any FilterBy values. FilterBy order does not affect the key. Each list entry has this shape:

[eventID, excludeValue, retrieveValue]

Use Delete to remove the list for the entity described by the request and source.

err := previous.Delete(ctx, c, previous.Request{
	Name:      "prev_amount",
	Namespace: "scoring",
	Ref:       "standard.user_id",
	Retrieve:  "amount",
	TTL:       time.Hour,
}, src)

Documentation

Index

Constants

View Source
const DefaultMaxCount = 100

DefaultMaxCount is the default maximum number of entries kept in the list.

View Source
const (
	SetPrevious = "previous"
)

Set names for previous-event storage.

Variables

This section is empty.

Functions

func Delete

func Delete(ctx context.Context, c deleteClient, req Request, src queries.Source) error

Delete removes the stored previous-event data for the given entity. The key is deterministic from (Name, Ref, source values, FilterBy).

Types

type Request

type Request struct {
	Name           string        // variable name
	Namespace      string        // FrogoDB namespace
	Ref            string        // source key for the reference entity (e.g., "standard.user_id")
	Retrieve       string        // source key for the value to retrieve
	Exclude        string        // source key for exclude matching (optional)
	FilterBy       []string      // additional grouping fields
	EventID        string        // current event ID (for dedup)
	MaxCount       int           // max entries in list (default 100)
	TTL            time.Duration // REQUIRED. Expiration for previous-event records.
	IncludeCurrent bool
}

Request describes a previous-event query.

func (Request) Validate

func (r Request) Validate() error

Validate checks that required fields are set.

type Result

type Result struct {
	Value any  // retrieved value from previous event
	Found bool // whether a matching previous event was found
}

Result holds the outcome of a previous-event query.

func Execute

func Execute(ctx context.Context, c operateClient, req Request, src queries.Source) (Result, error)

Execute writes the current event and reads the previous event value. The order of write vs read is controlled by IncludeCurrent.

func Read

func Read(ctx context.Context, c operateClient, req Request, src queries.Source) (Result, error)

Read performs a read-only query without writing the current event.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL