comfylite3

package module
v0.0.0-...-11b92c9 Latest Latest
Warning

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

Go to latest
Published: Apr 7, 2024 License: MIT Imports: 11 Imported by: 2

README

Comfylite3

Aren't you tired to be forced to write a different code for sqlite3 because you can't use it with multiple goroutines? I was. I disliked the constraints and changing my habits.

That's why comfylite3 exists! Just throw your queries at it and your sql will be executed*!

*: eventually!

Gopher Comfy

Install

go get -u github.com/davidroman0O/comfylite3

API

Memory or File or What you want!


// You want a default memory database
comfylite3.WithMemory()

// You want a default file database
comfylite3.File("comfyName.db")

// Feeling adventurous? You can!
comfylite3.WithConnection("file:/tmp/adventurousComfy.db?cache=shared")

What you can do

Very simplistic API, comfylite3 manage when to execute and you do as usual. I'm just judging how you will wrap that library!


// Create a new comfy database for `sqlite3`
comfyDB, _ := comfylite3.Comfy(comfylite3.WithMemory())

// Create future workload to cook
id := comfyDB.New(func(db *sql.DB) (interface{}, error) {
    _, err := db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
    return nil, err
})

// Ask it if it's done or not
if !comfyDB.IsDone(id) {
    fmt.Println("No no no! It is not done yet!")
}

// You will get what you send back! Error or not!
var result interface{}

// Eventually get your result
result = <-superComfy.WaitFor(id)

switch result.(type) {
	case error:
		fmt.Println("Oooh your query failed!", err)
}

Migrations

Migrations is important and sqlite is a specific type of database, and it support migrations!


// Let's imagine a set of migrations
var memoryMigrations []comfylite3.Migration = []comfylite3.Migration{
	comfylite3.NewMigration(
		1,
		"genesis",
		func(tx *sql.Tx) error {
			if _, err := tx.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"); err != nil {
				return err
			}
			if _, err := tx.Exec("CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES users(id))"); err != nil {
				return err
			}
			return nil
		},
		func(tx *sql.Tx) error {
			// undo previous up function
			if _, err := tx.Exec("DROP TABLE users"); err != nil {
				return err
			}
			if _, err := tx.Exec("DROP TABLE products"); err != nil {
				return err
			}
			return nil
		}),
	comfylite3.NewMigration(
		2,
		"new_table",
		func(tx *sql.Tx) error {
			if _, err := tx.Exec("ALTER TABLE products ADD COLUMN new_column TEXT"); err != nil {
				return err
			}
			if _, err := tx.Exec("CREATE TABLE new_table (id INTEGER PRIMARY KEY, name TEXT)"); err != nil {
				return err
			}
			return nil
		},
		func(tx *sql.Tx) error {
			// undo previous up function
			if _, err := tx.Exec("ALTER TABLE products DROP COLUMN new_column"); err != nil {
				return err
			}
			if _, err := tx.Exec("DROP TABLE new_table"); err != nil {
				return err
			}
			return nil
		}),
	comfylite3.NewMigration(
		3,
		"new_random",
		func(tx *sql.Tx) error {
			// remove new_column from products
			if _, err := tx.Exec("ALTER TABLE products DROP COLUMN new_column"); err != nil {
				return err
			}
			return nil
		},
		func(tx *sql.Tx) error {
			// add new_column to products
			if _, err := tx.Exec("ALTER TABLE products ADD COLUMN new_column TEXT"); err != nil {
				return err
			}
			return nil
		},
	),
}

// create and add your migrations
comfyDB, _ := comfylite3.Comfy(
		comfylite3.WithMemory(),
		comfylite3.WithMigration(memoryMigrations...),
		comfylite3.WithMigrationTableName("_migrations"), // even customize your migration table!
	)

// Migrations Up and Down are easy

// Up to the top!
if err := comfyDB.Up(context.Background()); err != nil {
	panic(err)
}

// Specify how many down you want to do
if err := comfyDB.Down(context.Background(), 1); err != nil {
	panic(err)
}

comfyDB.Version() // return all the existing versions []uint
comfyDB.Index() // return the current index of the migration
comfyDB.ShowTables() // return all table names
comfyDB.ShowColumns("name") // return columns data of one table

Example

package main 

import (
    "fmt"
    "github.com/davidroman0O/comfylite3"
)

func main() {

	var superComfy *comfylite3.ComfyDB
	var err error

    // Make yourself comfy
	if superComfy, err = comfylite3.Comfy(comfylite3.WithMemory()); err != nil {
		panic(err)
	}

	defer superComfy.Close()

    // Ask it to make a query for you
    ticket := superComfy.New(func(db *sql.DB) (interface{}, error) {
		_, err := db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
		return nil, err
	})

    // It will send you the result when it's finished cooking
	<-superComfy.WaitFor(ticket)

    // You don't have to change your habit, just use goroutines
    // `comfylite` will cook for you
    var futureTicket uint64
    go func() {
        futureTicket = superComfy.New(func(db *sql.DB) (interface{}, error) {
            _, err := db.Exec("INSERT INTO users (name) VALUES (?)", fmt.Sprintf("user%d", 1))
            return nil, err
        })
    }()

    if !superComfy.IsDone(futureTicket) {
        fmt.Println("No no no! It is not done yet!")
    }

    // Let comfylite3 cook!
    <-superComfy.WaitFor(futureTicket)
}

Or even with a more complex example! Here we want to compute the average amount of inserts per second:

package main 

import (
    "fmt"
    "github.com/davidroman0O/comfylite3"
)

func main() {
   
	var superComfy *comfylite3.ComfyDB
	var err error
	if superComfy, err = comfylite3.Comfy(comfylite3.WithMemory()); err != nil {
		panic(err)
	}

	ticket := superComfy.New(func(db *sql.DB) (interface{}, error) {
		_, err := db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
		return nil, err
	})
	<-superComfy.WaitFor(ticket)

	done := make(chan struct{})

	go func() {
		random := rand.New(rand.NewSource(time.Now().UnixNano()))
		for range 10000 {
			ticket := superComfy.New(func(db *sql.DB) (interface{}, error) {
				_, err := db.Exec("INSERT INTO users (name) VALUES (?)", fmt.Sprintf("user%d", 1))
				return nil, err
			})
			<-superComfy.WaitFor(ticket)
			// simulate random insert with a random sleep
			time.Sleep(time.Duration(random.Intn(5)) * time.Millisecond)
		}
		done <- struct{}{}
	}()

	// Let's measure how many records per second
	metrics := []int{}

	ticker := time.NewTicker(1 * time.Second)

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	compute := true
	for compute {
		select {
		case <-done:
			ticker.Stop()
			cancel()
			compute = false
		case <-ctx.Done():
			ticker.Stop()
			cancel()
			compute = false
		case <-ticker.C:
			ticket := superComfy.New(func(db *sql.DB) (interface{}, error) {
				rows, err := db.Query("SELECT COUNT(*) FROM users")
				if err != nil {
					return nil, err
				}
				defer rows.Close()

				var count int
				if rows.Next() {
					err = rows.Scan(&count)
					if err != nil {
						return nil, err
					}
				}

				return count, err
			})
			result := <-superComfy.WaitFor(ticket)
			metrics = append(metrics, result.(int))
		}
	}

	total := 0
	for _, value := range metrics {
		total += value
	}
	average := float64(total) / float64(len(metrics))
	fmt.Printf("Average: %.2f\n", average)
}

Enjoy freeing yourself from database is locked!

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Column

type Column struct {
	CID       int
	Name      string
	Type      string
	NotNull   bool
	DfltValue *string
	Pk        bool
}

Properties of one column in a table. Columns: cid name type notnull dflt_value pk

type ComfyDB

type ComfyDB struct {
	// contains filtered or unexported fields
}

ComfyDB is a wrapper around sqlite3 that provides a simple API for executing SQL queries with goroutines. It also provides a simple API for managing migrations. Goodbye frustrations with sqlite3 and goroutines.

func Comfy

func Comfy(opts ...ComfyOption) (*ComfyDB, error)

Create a new ComfyLite3 wrapper around sqlite3. Instanciate a scheduler to process your queries.

func (*ComfyDB) BeginTx

func (c *ComfyDB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)

func (*ComfyDB) Being

func (c *ComfyDB) Being() (*sql.Tx, error)

func (*ComfyDB) Clear

func (c *ComfyDB) Clear(id uint64)

Clear the buffer and the channel for a specific workID.

func (*ComfyDB) Close

func (c *ComfyDB) Close()

Close the database connection.

func (*ComfyDB) Conn

func (c *ComfyDB) Conn(ctx context.Context) (*sql.Conn, error)

func (*ComfyDB) Down

func (c *ComfyDB) Down(ctx context.Context, amount int) error

Migrate down using the amount of iterations to rollback.

func (*ComfyDB) Driver

func (c *ComfyDB) Driver() driver.Driver

func (*ComfyDB) Exec

func (c *ComfyDB) Exec(query string, args ...interface{}) (sql.Result, error)

func (*ComfyDB) ExecContext

func (c *ComfyDB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

func (*ComfyDB) Index

func (c *ComfyDB) Index() ([]uint, error)

Get all versions of the migrations.

func (*ComfyDB) IsDone

func (c *ComfyDB) IsDone(workID uint64) bool

Ask if a workID (your query) is done.

func (*ComfyDB) Migrations

func (c *ComfyDB) Migrations() ([]Migration, error)

Get all migrations.

func (*ComfyDB) New

func (c *ComfyDB) New(fn SqlFn) uint64

func (*ComfyDB) Ping

func (c *ComfyDB) Ping() error

implement Ping() error of sql.DB with Comfy

func (*ComfyDB) PingContext

func (c *ComfyDB) PingContext(ctx context.Context) error

func (*ComfyDB) Prepare

func (c *ComfyDB) Prepare(query string) (*sql.Stmt, error)

func (*ComfyDB) PrepareContext

func (c *ComfyDB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

func (*ComfyDB) Query

func (c *ComfyDB) Query(query string, args ...interface{}) (*sql.Rows, error)

func (*ComfyDB) QueryContext

func (c *ComfyDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)

func (*ComfyDB) QueryRow

func (c *ComfyDB) QueryRow(query string, args ...interface{}) *sql.Row

func (*ComfyDB) QueryRowContext

func (c *ComfyDB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row

func (*ComfyDB) SetConnMaxIdleTime

func (c *ComfyDB) SetConnMaxIdleTime(d time.Duration)

func (*ComfyDB) SetConnMaxLifetime

func (c *ComfyDB) SetConnMaxLifetime(d time.Duration)

func (*ComfyDB) SetMaxIdleConns

func (c *ComfyDB) SetMaxIdleConns(n int)

func (*ComfyDB) SetMaxOpenConns

func (c *ComfyDB) SetMaxOpenConns(n int)

func (*ComfyDB) ShowColumns

func (c *ComfyDB) ShowColumns(table string) ([]Column, error)

Show all columns in a table.

func (*ComfyDB) ShowTables

func (c *ComfyDB) ShowTables() ([]string, error)

Show all tables in the database. Returns a slice of the names of the tables.

func (*ComfyDB) Stats

func (c *ComfyDB) Stats() sql.DBStats

func (*ComfyDB) Up

func (c *ComfyDB) Up(ctx context.Context) error

Migrate up all the available migrations.

func (*ComfyDB) Version

func (c *ComfyDB) Version() (uint, error)

Get current version of the migrations.

func (*ComfyDB) WaitFor

func (c *ComfyDB) WaitFor(workID uint64) <-chan interface{}

Wait or return the result of a workID (your query).

type ComfyOption

type ComfyOption func(*ComfyDB)

func WithBuffer

func WithBuffer(size int64) ComfyOption

WithBuffer sets the size of the ring buffer which is used by the scheduler to process your queries. (default=1024) If the buffer is full, it will create a second one of that size value automatically.

func WithConnection

func WithConnection(conn string) ComfyOption

WithConnection sets a custom connection string for the database.

func WithMemory

func WithMemory() ComfyOption

WithMemory sets the database to be in-memory.

func WithMigration

func WithMigration(migrations ...Migration) ComfyOption

Records your migrations for your database.

func WithMigrationTableName

func WithMigrationTableName(name string) ComfyOption

WithMigrationTableName sets the name of the migration table.

func WithPath

func WithPath(path string) ComfyOption

WithPath sets the path of the database file.

type Migration

type Migration struct {
	Version uint
	Label   string
	Up      func(tx *sql.Tx) error
	Down    func(tx *sql.Tx) error
}

func NewMigration

func NewMigration(version uint, label string, up, down func(tx *sql.Tx) error) Migration

Create a new migration with a version, label, and up and down functions.

type RingBuffer

type RingBuffer[T any] struct {
	// contains filtered or unexported fields
}

func Buffer

func Buffer[T any](size int64) *RingBuffer[T]

func (*RingBuffer[T]) Len

func (rb *RingBuffer[T]) Len() int64

func (*RingBuffer[T]) Pop

func (rb *RingBuffer[T]) Pop() (T, bool)

func (*RingBuffer[T]) PopN

func (rb *RingBuffer[T]) PopN(n int64) ([]T, bool)

func (*RingBuffer[T]) Push

func (rb *RingBuffer[T]) Push(item T)

type SqlFn

type SqlFn func(db *sql.DB) (interface{}, error)

Callback provided by a developer to be executed when the scheduler is ready for it

Jump to

Keyboard shortcuts

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