Flash is a lightweight Go library for managing real-time PostgreSQL changes using event management.
This library is currently under active development.
Features and APIs may change.
Contributions and feedback are welcome!
- Features
- Installation
- Usage
- Advanced Features
- Planned Features
- Drivers
- Upgrade from Old Structure
- Contributing
- Credits
- License
- ✅ Start/Stop listening during runtime.
- ✅ Supports common PostgreSQL events: Insert, Update, Delete, Truncate.
- ✅ Driver interfaces for creating new drivers.
- ✅ Parallel Callback execution using goroutine
- ✅ Listen for changes in specific columns, not the entire row. (see Advanced Features)
- ✅ Listen changes using WAL replication (see Drivers)
To install the library, run:
go get -u github.com/quix-labs/flash@main # Actually main is used for development
go get -u github.com/quix-labs/flash/drivers/trigger@main # Show below to for other drivers
# Write your main package
go mod tidy # Actually needed to download nested dependencies - Working on proper pre-install during previous go get
Here's a basic example of how to use Flash:
package main
import (
"fmt"
"github.com/quix-labs/flash"
"github.com/quix-labs/flash/drivers/trigger"
"os"
"os/signal"
)
func main() {
// Example with listener and client setup
postsListener, _ := flash.NewListener(&flash.ListenerConfig{Table: "public.posts"})
postsListener.On(flash.OperationAll, func(event flash.Event) {
switch typedEvent := event.(type) {
case *flash.InsertEvent:
fmt.Printf("insert - new: %+v\n", typedEvent.New)
case *flash.UpdateEvent:
fmt.Printf("update - old: %+v - new: %+v\n", typedEvent.Old, typedEvent.New)
case *flash.DeleteEvent:
fmt.Printf("delete - old: %+v \n", typedEvent.Old)
case *flash.TruncateEvent:
fmt.Printf("truncate \n")
}
})
// Create client
flashClient, _ := flash.NewClient(&flash.ClientConfig{
DatabaseCnx: "postgresql://devuser:devpass@localhost:5432/devdb",
Driver: trigger.NewDriver(&trigger.DriverConfig{}),
})
flashClient.Attach(postsListener)
// Start listening
go flashClient.Start()
defer flashClient.Close()
// Wait for interrupt signal (Ctrl+C)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
<-interrupt
fmt.Println("Program terminated.")
}
For more detailed examples, check out the following files:
- Debug queries
- Trigger insert events on table
- Trigger all events on table
- Listen for specific fields
- Parallel Callback
When you define a primary key, instead of receiving an update event when the column changes, you will receive two events:
- A delete event with the old value of this column (and other fields).
- An insert event with the new value of this column (and other fields).
You can configure conditions, and if a database row does not match the criteria, you will not receive any event.
In the case of an update:
- If the row previously matched the criteria but the new row does not, you will receive a delete event.
- If the row previously did not match the criteria but the new row does, you will receive an insert event.
Ability to listen only to certain columns in your table. If no changes occur in one of these columns, you will not receive any event.
Some of these features may be incompatible with your driver.
Check drivers/README.md to see if the driver you have chosen supports these features.
The following features are planned for future implementation:
- ⏳ Support for conditional listens.
Operator | trigger | wal_logical |
---|---|---|
equals | ✅ | ✅ |
neq | ❌ | ❌ |
lt | ❌ | ❌ |
lte | ❌ | ❌ |
gte | ❌ | ❌ |
not null | ❌ | ❌ |
is null |
- ⏳ Handling custom primary for fake insert/delete when change appears
- ⬜ Remove client in favor of direct listener start
- ⬜ Support attaching/detaching new listener during runtime.
- ⬜ Tests implementation
- ... any suggestions is welcome.
You can see all drivers details here
In the previous structure, our project was divided into three distinct sub-modules, making it cumbersome to manage and integrate changes.
We have now merged these sub-modules into a single, unified module: github.com/quix-labs/flash.
This consolidation simplifies the codebase and streamlines development.
-
Unified Repository:
The previously separate sub-modules are now combined into one repository. This allows for easier dependency management and a more cohesive development process.
-
Separate Driver Installation:
While the core functionality is now in one place, the drivers need to be installed separately. This modular approach ensures that you only include what you need, keeping your projects lightweight.
-
No default driver:
By default, we are previously using trigger driver, to keep user informed, the user require now to instanciate the driver and pass it in ClientConfig
- Replace all your
client.NewClient(&type.ClientConfig{})
byflash.NewClient(&flash.ClientConfig{})
- Replace all your
listeners.NewListener(types.ListenerConfig{})
byflash.NewListener(&flash.ListenerConfig{})
- Instantiate the
Driver
in your codebase and pass it toflash.ClientConfig{}
package main
import (
"github.com/quix-labs/flash"
"github.com/quix-labs/flash/drivers/trigger"
)
func main() {
// Instantiation of driver is now required
driver := trigger.NewDriver(&trigger.DriverConfig{})
client := flash.NewClient(&flash.ClientConfig{
Driver: driver,
})
// Instead of listeners.NewListener, use flash.NewListener
listener := flash.NewListener(&flash.ListenerConfig{})
// Your additional code here
}
- Fork the repository.
- Create a new branch for your feature or bugfix.
- Commit your changes.
- Push your branch.
- Create a pull request.
The MIT License (MIT). Please see License File for more information.