Documentation ¶
Overview ¶
Package pm is a process manager with an HTTP monitoring/control interface.
To this package, processes or tasks are user-defined routines within a running program. This model is particularly suitable to server programs, with independent client-generated requests for action. The library would keep track of all such tasks and make the information available through HTTP, thus providing valuable insight into the running application. The client can also ask for a particular task to be canceled.
Using pm starts by calling ListenAndServe() in a separate routine, like so:
go pm.ListenAndServe(":8081")
Note that pm's ListenAndServe() will not return by itself. Nevertheless, it will if the underlying net/http's ListenAndServe() does. So it's probably a good idea to wrap that call with some error checking and retrying for production code.
Tasks to be tracked must be declared with Start(); that's when the identifier gets linked to them. An optional set of (arbitrary) attributes may be provided, that will get attached to the running task and be reported to the HTTP client tool as well. (Think of host, method and URI for a web server, for instance.) This could go like this:
pm.Start(requestID, nil, &map[string]interface{}{ "host": req.RemoteAddr, "method": req.Method, "uri": req.RequestURI, }) defer pm.Done(requestID)
Note the deferred pm.Done() call. That's essential to mark the task as finished and release resources, and has to be called no matter if the task succeeded or not. That's why it's a good idea to use Go's defer mechanism. In fact, the protection from cancel-related panics (see StopCancelPanic below) does NOT work if Done() is called in-line (i.e., not deferred) due to properties of recover().
An HTTP client issuing a GET call to /procs/ would receive a JSON response with a snapshot of all currently running tasks. Each one will include the time when it was started (as per the server clock) as well as the complete set of attributes provided to Start(). Note that to avoid issues with clock skews among servers, the current time for the server is returned as well.
The reply to the /procs/ GET also includes a status. When tasks start they are set to "init", but they may change their status using pm's Status() function. Each call will record the change and the HTTP client will receive the last status available, together with the time it was set. Furthermore, the client may GET /procs/<id>/history instead (where <id> is the task identifier) to have a complete history for the given task, including all status changes with their time information. However, note that task information is completely recycled as soon as a task is Done(). Hence, client applications should be prepared to receive a not found reply even if they've just seen the task in a /procs/ GET result.
Given the lack of a statement in Go to kill a routine, cancellations are implemented as panics. A DELETE call to /procs/<id> will mark the task with the given identifier as cancel-pending. Nevertheless, the task will never notice until it reaches another Status() call, which is by definition a cancellation point. Calls to Status() either return having set the new status, or panic with an error of type CancelErr. Needless to say, the application should handle this gracefully or will otherwise crash. Programs serving multiple requests will probably be already protected, with a recover() call at the level where the routine was started. But if that's not the case, or if you simply want the panic to be handled transparently, you may use this:
pm.SetOptions(ProclistOptions{ StopCancelPanic: true })
When the StopCancelPanic option is set (which is NOT the default) Done() will recover a panic due to a cancel operation. In such a case, the routine running that code will jump to the next statement after the invocation to the function that called Start(). (Read it again.) In other words, stack unfolding stops at the level where Done() was deferred. Notice, though, that this behavior is specifically tailored to panics raising from pending cancellation requests. Should any other panic arise for any reason, it will continue past Done() as usual. So will panics due to cancel requests if StopCancelPanic is not set.
Options set with pm.SetOptions() work on a global scope for the process list. Alternatively, you may provide a specific set for some task by using the options argument in the Start() call. If none is given, pm will grab the current global values at the time Start() was invoked. Task-specific options may come handy when, for instance, some tasks should be resilient to cancellation. Setting the ForbidCancel option makes the task effectively immune to cancel requests. Attempts by clients to cancel one of such tasks will inevitably yield an error message with no other effects. Set that with the global SetOptions() function and none of your tasks will cancel, ever.
HTTP clients can learn about pending cancellation requests. Furthermore, if a client request happens to be handled between the task is done/canceled and resource recycling (a VERY tiny time window), then the result would include one of these as the status: "killed", "aborted" or "ended", if it was respectively canceled, died out of another panic (not pm-related) or finished successfully. The "killed" status may optionally add a user-defined message, provided through the HTTP /procs/<id> DELETE method.
For the cancellation feature to be useful, applications should collaborate. Go lacks a mechanism to cancel arbitrary routines (it even lacks identifiers for them), so programs willing to provide the feature must be willing to help. It's a good practice to add cancellation points every once in a while, particularly when lengthy operations are run. However, applications are not expected to change status that frequently. This package provides the function CheckCancel() for that. It works as a cancellation point by definition, without messing with the task status, nor leaving a trace in history.
Finally, please note that cancellation requests yield panics in the same routine that called Start() with that given identifier. However, it's not unusual for servers to spawn additional Go routines to handle the same request. The application is responsible of cleaning up, if there are additional resources that should be recycled. The proper way to do this is by catching CancelErr type panics, cleaning-up and then re-panic, i.e.:
func handleRequest(requestId string) { pm.Start(requestId, map[string]interface{}{}) defer pm.Done(requestId) defer func() { if e := recover(); e != nil { if _, canceled := e.(CancelErr); canceled { // do your cleanup here } panic(e) // re-panic with same error (cancel or not) } }() // your application code goes here }
Index ¶
- Constants
- Variables
- func CheckCancel(id string)
- func DelAttribute(id, name string)
- func Done(id string)
- func Kill(id, message string) error
- func ListenAndServe(addr string) error
- func SetAttribute(id, name string, value interface{})
- func SetOptions(opts ProclistOpts)
- func Start(id string, opts *ProcOpts, attrs *map[string]interface{})
- func Status(id, status string)
- type CancelErr
- type CancelRequest
- type HistoryDetail
- type HistoryResponse
- type ProcDetail
- type ProcOpts
- type ProcResponse
- type Proclist
- func (pl *Proclist) CheckCancel(id string)
- func (pl *Proclist) DelAttribute(id, name string)
- func (pl *Proclist) Done(id string)
- func (pl *Proclist) GetAll() []ProcDetail
- func (pl *Proclist) GetHistory(id string) ([]HistoryDetail, error)
- func (pl *Proclist) Kill(id, message string) error
- func (pl *Proclist) ListenAndServe(addr string) error
- func (pl *Proclist) Options() ProclistOpts
- func (pl *Proclist) SetAttribute(id, name string, value interface{})
- func (pl *Proclist) SetOptions(opts ProclistOpts)
- func (pl *Proclist) Start(id string, opts *ProcOpts, attrs *map[string]interface{})
- func (pl *Proclist) Status(id, status string)
- type ProclistOpts
Constants ¶
const ( HeaderContentType = "Content-Type" MediaJSON = "application/json" )
Variables ¶
var ( ErrForbidden = errors.New("forbidden") ErrNoSuchProcess = errors.New("no such process") )
Functions ¶
func CheckCancel ¶
func CheckCancel(id string)
CheckCancel introduces a cancellation point just like Status() does, but without changing the task status, nor adding an entry to history.
func DelAttribute ¶
func DelAttribute(id, name string)
DelAttribute deletes an attribute for the task given by id. Unrecognized task identifiers are silently skipped.
func Done ¶
func Done(id string)
Done marks the end of a task, writing to history depending on the outcome (i.e., aborted, killed or finished successfully). This function releases resources associated with the process, thus making the id available for use by another task. It also stops panics raising from cancellation requests, but only when the StopCancelPanic option is set AND Done is called with a defer statement.
func Kill ¶
Kill sets a cancellation request to the task with the given identifier, that will be effective as soon as the routine running that task hits a cancellation point. The (optional) message will be included in the CancelErr object used for panic.
func ListenAndServe ¶
ListenAndServe starts an HTTP server at the given address (localhost:80 by default, as results from the underlying net/http implementation).
func SetAttribute ¶
func SetAttribute(id, name string, value interface{})
SetAttribute sets an application-specific attribute for the task given by id. Unrecognized identifiers are silently skipped. Duplicate attribute names for the task overwrite the previously set value.
func SetOptions ¶
func SetOptions(opts ProclistOpts)
SetOptions sets the options to be used for this Proclist.
func Start ¶
Start marks the beginning of a task at the default Proclist. All attributes are recorded but not handled internally by the package. They will be mapped to JSON and provided to HTTP clients for this task. It's the caller's responsibility to provide different identifiers for separate tasks. An id can only be reused after the task previously using it is over. If process options are not provided (nil), Start() will snapshot the global options for the process list set by SetOptions().
Types ¶
type CancelErr ¶
type CancelErr string
Type CancelErr is the type used for cancellation-induced panics.
type CancelRequest ¶
type CancelRequest struct {
Message string `json:"message"`
}
CancelRequest is the request body resulting from Kill().
type HistoryDetail ¶
HistoryDetail encodes one entry from the process' history.
type HistoryResponse ¶
type HistoryResponse struct { History []HistoryDetail `json:"history"` ServerTime time.Time `json:"serverTime"` }
HistoryResponse is the response for a GET to /proc/<id>/history.
type ProcDetail ¶
type ProcDetail struct { Id string `json:"id"` Attrs map[string]interface{} `json:"attrs,omitempty"` ProcTime time.Time `json:"procTime"` StatusTime time.Time `json:"statusTime"` Status string `json:"status"` Cancelling bool `json:"cancelling,omitempty"` }
Type ProcDetail encodes a full process list from the server, including an attributes array with application-defined names/values.
type ProcOpts ¶
type ProcOpts struct { StopCancelPanic bool // Stop cancel-related panics at Done() ForbidCancel bool // Forbid cancellation requests }
Type ProcOpts provides options for the process.
type ProcResponse ¶
type ProcResponse struct { Procs []ProcDetail `json:"procs"` ServerTime time.Time `json:"serverTime"` }
ProcResponse is the response for a GET to /proc.
type Proclist ¶
type Proclist struct {
// contains filtered or unexported fields
}
Type Proclist is the main type for the process-list. You may have as many as you wish, each with it's own HTTP server, but that's probably useful only in a handful of cases. The typical use of this package is through the default Proclist object (DefaultProclist) and package-level functions. The zero value for the type is a Proclist ready to be used.
var DefaultProclist Proclist
This is the default Proclist set for the package. Package-level operations use this list.
func (*Proclist) CheckCancel ¶
CheckCancel introduces a cancellation point just like Status() does, but without changing the task status, nor adding an entry to history.
func (*Proclist) DelAttribute ¶
DelAttribute deletes an attribute for the task given by id. Unrecognized task identifiers are silently skipped.
func (*Proclist) Done ¶
Done marks the end of a task, writing in history depending on the outcome (i.e., aborted, killed or finished successfully). This function releases resources associated with the process, thus making the id available for use by another task. It also stops panics raising from cancellation requests, but only when the StopCancelPanic option is set AND Done is called with a defer statement.
func (*Proclist) GetAll ¶
func (pl *Proclist) GetAll() []ProcDetail
func (*Proclist) GetHistory ¶
func (pl *Proclist) GetHistory(id string) ([]HistoryDetail, error)
func (*Proclist) Kill ¶
Kill sets a cancellation request to the task with the given identifier, that will be effective as soon as the routine running that task hits a cancellation point. The (optional) message will be included in the CancelErr object used for panic.
func (*Proclist) ListenAndServe ¶
ListenAndServe starts an HTTP server at the given address (localhost:80 by default, as results from the underlying net/http implementation).
func (*Proclist) Options ¶
func (pl *Proclist) Options() ProclistOpts
Options returns the options set for this Proclist.
func (*Proclist) SetAttribute ¶
SetAttribute sets an application-specific attribute for the task given by id. Unrecognized identifiers are silently skipped. Duplicate attribute names for the task overwrite the previously set value.
func (*Proclist) SetOptions ¶
func (pl *Proclist) SetOptions(opts ProclistOpts)
SetOptions sets the options to be used for this Proclist.
func (*Proclist) Start ¶
Start marks the beginning of a task at this Proclist. All attributes are recorded but not handled internally by the package. They will be mapped to JSON and provided to HTTP clients for this task. It's the caller's responsibility to provide different identifiers for separate tasks. An id can only be reused after the task previously using it is over. If process options are not provided (nil), Start() will snapshot the global options for the process list set by SetOptions().
type ProclistOpts ¶
type ProclistOpts struct { StopCancelPanic bool // Stop cancel-related panics at Done() ForbidCancel bool // Forbid cancellation requests }
Type ProclistOpts provides all options to be set for a Proclist. Options shared with ProcOpts act as defaults in case no options are provided in a task's call to Start().
func Options ¶
func Options() ProclistOpts
Options returns the options set for the default Proclist.