driver

package
v8.0.0-...-33749c5 Latest Latest
Warning

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

Go to latest
Published: Mar 10, 2025 License: Apache-2.0 Imports: 12 Imported by: 2

Documentation

Overview

Package driver provides the foundational interfaces for implementing multitenancy support within database systems. It outlines the necessary components for managing tenant lifecycles, including onboarding, offboarding, and handling shared resources. These interfaces serve as a contract for database management systems (DBMS) to ensure consistent multitenant operations, abstracting the complexities of tenant-specific data handling. Developers should integrate these interfaces with their database solutions to enable scalable and isolated data management for each tenant, leveraging the flexibility and power of GORM for ORM operations. This package is intended for use by developers implementing multitenant architectures, with the core application logic residing elsewhere.

Index

Examples

Constants

View Source
const PublicSchemaEnvVar = "GMT_PUBLIC_SCHEMA_NAME"

PublicSchemaEnvVar is the environment variable that contains the name of the public schema.

Variables

View Source
var (
	// ErrInvalidMigration is returned when an invalid migration is detected.
	ErrInvalidMigration = errors.Join(
		errors.New("invalid migration"),
		errors.New("please ensure you're using MigrateSharedModels or MigrateTenantModels instead of calling AutoMigrate directly"),
	)
)

Functions

func ModelsToInterfaces

func ModelsToInterfaces(models []TenantTabler) []interface{}

ModelsToInterfaces converts a slice of TenantTabler models to a slice of interface{}.

func ParseDSNQueryParams

func ParseDSNQueryParams[T any](dsn string) (params T, err error)

ParseDSNQueryParams parses the query parameters from the dsn string and decodes them into a generic type T (non-pointer struct). It returns the parsed parameters and an error if any occurred.

The dsn string should be in the format of a standard URL with query parameters. For example: "user:password@tcp(localhost:3306)/dbname?charset=utf8&parseTime=True&loc=Local".

Example
package main

import (
	"fmt"
	"time"

	"github.com/thisaftermath/gorm-multitenancy/v8/pkg/driver"
)

func main() {
	type backoffOptions struct {
		MaxRetries  int           `mapstructure:"max_retries"`
		Interval    time.Duration `mapstructure:"retry_interval"`
		MaxInterval time.Duration `mapstructure:"retry_max_interval"`
	}

	type dsnOptions struct {
		DisableRetry bool           `mapstructure:"disable_retry"`
		Retry        backoffOptions `mapstructure:",squash"`
	}

	dsn := "mysql://user:password@tcp(localhost:3306)/dbname?disable_retry=true&max_retries=6&retry_interval=2s&retry_max_interval=30s"
	opts, err := driver.ParseDSNQueryParams[dsnOptions](dsn)
	if err != nil {
		panic(err)
	}

	// Use the parsed options.
	fmt.Printf("DisableRetry: %v\n", opts.DisableRetry)
	fmt.Printf("MaxRetries: %d\n", opts.Retry.MaxRetries)
	fmt.Printf("Interval: %s\n", opts.Retry.Interval)
	fmt.Printf("MaxInterval: %s\n", opts.Retry.MaxInterval)

}
Output:

DisableRetry: true
MaxRetries: 6
Interval: 2s
MaxInterval: 30s

func PublicSchemaName

func PublicSchemaName() string

PublicSchemaName returns the name of the public schema as defined by the PublicSchemaEnvVar environment variable, defaulting to "public" if the variable is not set. This schema name is used to identify shared models.

Types

type DBFactory

type DBFactory interface {
	// RegisterModels registers GORM model structs for multitenancy support within a specific database.
	// It prepares models for tenant-specific operations and is idempotent. Returns an error if registration fails.
	RegisterModels(ctx context.Context, db *gorm.DB, models ...TenantTabler) error

	// MigrateSharedModels ensures shared data structures are set up and up-to-date within a specific database,
	// maintaining integrity and compatibility of shared data across tenants. Returns an error if migration fails.
	MigrateSharedModels(ctx context.Context, db *gorm.DB) error

	// MigrateTenantModels prepares and updates data structures for a specific tenant within a specific database,
	// handling onboarding and ongoing schema evolution. Returns an error if setup or migration fails.
	MigrateTenantModels(ctx context.Context, db *gorm.DB, tenantID string) error

	// OffboardTenant cleans up the database for a removed tenant within a specific database, supporting clean offboarding.
	// Returns an error if the process fails.
	OffboardTenant(ctx context.Context, db *gorm.DB, tenantID string) error

	// UseTenant configures the database for operations specific to a tenant within a specific database, abstracting
	// database-specific operations for tenant context configuration. Returns a reset function to revert the database context
	// and an error if the operation fails.
	UseTenant(ctx context.Context, db *gorm.DB, tenantID string) (reset func() error, err error)

	// CurrentTenant returns the identifier for the current tenant context within a specific database or an empty string
	// if no context is set.
	CurrentTenant(ctx context.Context, db *gorm.DB) string
}

DBFactory defines operations for managing the lifecycle of tenants within a multitenant database architecture. It abstracts tenant-specific operations such as onboarding, offboarding, and managing shared resources. Implementations of this interface are designed to integrate multitenancy support with GORM's features, providing a consistent approach across different database backends.

type ModelRegistry

type ModelRegistry struct {
	SharedModels []TenantTabler // SharedModels contains the models that are shared across tenants.
	TenantModels []TenantTabler // TenantModels contains the models that are specific to a tenant.
}

ModelRegistry holds the models registered for multitenancy support, categorizing them into shared and tenant-specific models. Not intended for direct use in application code.

func NewModelRegistry

func NewModelRegistry(models ...TenantTabler) (*ModelRegistry, error)

NewModelRegistry creates and initializes a new ModelRegistry with the provided models, categorizing them into shared and tenant-specific based on their characteristics. It returns an error if any model fails validation. Not intended for direct use in application code.

type TenantTabler

type TenantTabler interface {
	schema.Tabler
	// IsSharedModel returns true if the model is shared across tenants, indicating
	// it does not belong to a single tenant.
	IsSharedModel() bool
}

TenantTabler defines an interface for models within a multi-tenant architecture, extending schema.Tabler. Models must define their table name and indicate if they are shared across tenants. Crucial for differentiating between shared and tenant-specific data.

Implementations of this interface should return true for [IsSharedModel] if the model is shared across tenants, indicating it does not belong to a single tenant.

Example of a shared model:

type User struct {
	gorm.Model
	Email string
}

func (User) TableName() string { return "public.users" }
func (User) IsSharedModel() bool { return true }

Example of a tenant-specific model:

type Product struct {
	gorm.Model
	TenantID string
	Name     string
}

func (Product) TableName() string { return "products" }
func (Product) IsSharedModel() bool { return false }

type URL

type URL struct {
	*stdurl.URL
	// contains filtered or unexported fields
}

URL is a wrapper around the standard stdurl.URL type that includes the original URL string. It is designed to handle additional special cases for driver URL-like strings, such as the @tcp(localhost:3306) format used by MySQL.

func ParseURL

func ParseURL(rawURL string) (*URL, error)

ParseURL parses the provided rawURL string and returns a new URL instance. This function is a convenience wrapper around the URL.Parse method, allowing for direct parsing of raw URL strings into the URL type.

Example
package main

import (
	"fmt"

	"github.com/thisaftermath/gorm-multitenancy/v8/pkg/driver"
)

func main() {
	dsn := "mysql://user:password@tcp(localhost:3306)/dbname"
	u, err := driver.ParseURL(dsn)
	if err != nil {
		panic(err)
	}

	fmt.Println("Scheme:", u.Scheme)
	fmt.Println("Host:", u.Host)
	fmt.Println("Path:", u.Path)
	fmt.Println("User:", u.User.String())
	fmt.Println("Raw URL:", u.Raw())
	fmt.Println("Sanitized URL:", u.String())

}
Output:

Scheme: mysql
Host: localhost:3306
Path: /dbname
User: user:password
Raw URL: mysql://user:password@tcp(localhost:3306)/dbname
Sanitized URL: mysql://user:password@localhost:3306/dbname

func (*URL) Parse

func (u *URL) Parse(rawURL string) (*URL, error)

Parse parses the provided rawURL string and returns a new URL instance. It normalizes the URL to handle special cases specific to database driver URLs.

func (*URL) Raw

func (u *URL) Raw() string

Raw returns the original unsanitized URL string before any normalization.

Jump to

Keyboard shortcuts

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