Hotswap
provides you a complete solution to reload your go
code without restarting your server, interrupting or blocking any ongoing procedure. Hotswap
is built upon the plugin mechanism.
- Reload your code like a breeze
- Run different versions of a plugin in complete isolation
- Invoke an in-plugin function from its host program with
Plugin.InvokeFunc()
- Expose in-plugin data and functions with
PluginManager.Vault.Extension
and/orPluginManager.Vault.DataBag
- Handle asynchronous jobs using the latest code with
live function
,live type
, andlive data
- Link plugins statically for easy debugging
- Expose functions to other plugins with
Export()
- Depend on other plugins with
Import()
go install github.com/edwingeng/hotswap/cli/hotswap
Usage:
hotswap build [flags] <pluginDir> <outputDir> -- [buildFlags]
Examples:
hotswap build plugin/foo bin
hotswap build -v plugin/foo bin -- -race
hotswap build --staticLinking plugin/foo plugin
Flags:
--debug enable debug mode
--exclude string go-regexp matching files to exclude from included
--goBuild if --goBuild=false, skip the go build procedure (default true)
-h, --help help for build
--include string go-regexp matching files to include in addition to .go files
--leaveTemps do not delete temporary files
--livePrefix string case-insensitive name prefix of live functions and live types (default "live_")
--staticLinking generate code for static linking instead of building a plugin
-v, --verbose enable verbose mode
You can find these examples under the demo
directory. To have a direct experience, start a server with run.sh
and reload its plugin(s) with reload.sh
.
hello
demonstrates the basic usage, including how to organize host and plugin, how to build them, how to load plugin on server startup, how to useInvokeEach
, and how to reload.extension
shows how to define a custom extension and how to usePluginManager.Vault.Extension
. A small hint:WithExtensionNewer()
livex
is somewhat complex. It shows how to work withlive function
,live type
, andlive data
.slink
is an example of plugin static-linking, with which debugging a plugin with a debugger (delve) under MacOS and Windows becomes possible.trine
is the last example. It demonstrates the plugin dependency mechanism.
A plugin must have the following functions defined in its root package.
// OnLoad gets called after all plugins are successfully loaded and before the Vault is initialized.
func OnLoad(data interface{}) error {
return nil
}
// OnInit gets called after the execution of all OnLoad functions. The Vault is ready now.
func OnInit(sharedVault *vault.Vault) error {
return nil
}
// OnFree gets called at some time after a reload.
func OnFree() {
}
// Export returns an object to export to other plugins.
func Export() interface{} {
return nil
}
// Import returns an object indicating the dependencies of the plugin.
func Import() interface{} {
return nil
}
// InvokeFunc invokes the specified function.
func InvokeFunc(name string, params ...interface{}) (interface{}, error) {
return nil, nil
}
// Reloadable indicates whether the plugin is reloadable.
func Reloadable() bool {
return true
}
1. Reloadable
2. Export
3. Import
4. OnLoad
5. Vault Initialization
6. OnInit
- Build your host program with the environmental variable
CGO_ENABLED=1
and the-trimpath
flag. - Do not define any global variable in a reloadable plugin unless it can be discarded at any time or it actually never changes.
- Do not create any long-running goroutine in a plugin, it's error-prone.
- The same type in different versions of a plugin are actually not the same at runtime. Use
live function
,live type
, andlive data
to avoid the trap. - The code of your host program should never import any package of any plugin and the code of a plugin should never import any package of other plugins.
- Old versions won't be removed from the memory due to the limitation of golang plugin. However,
Hotswap
offers you a chance, theOnFree
function, to clear caches. - It is required to manage your code with
git
andgo module
. - It is highly recommended to keep the code of your host program and all its plugins in a same repository.
live function
is a type of function whose name is prefixed withlive_
(case-insensitive). Live functions are automatically collected and stored inPluginManager.Vault.LiveFuncs
. For example:
func live_Foo(jobData live.Data) error {
return nil
}
live type
is a type of struct whose name is prefixed withlive_
(case-insensitive). Live types are automatically collected and stored inPluginManager.Vault.LiveTypes
. For example:
type Live_Bar struct {
N int
}
live data
is a type guardian. You can convert your data into alive data
object when scheduling an asynchronous job and restore your data from thelive data
object when handling the job.- See the demo
livex
for details.