-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/note feature transfer #17
Conversation
…e interface method
connectionmgr.go
Outdated
// ConnectionManager manages initialization and shutdown of long lived connections. | ||
// Each connection object must match the Initializer interface. Initialization happens | ||
// in parallel. The waitgroup can be used to wait util all connections are initialized. | ||
// Close kick off the proper shutdown of all managed connections. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/kick/kicks
connectionmgr.go
Outdated
@@ -0,0 +1,72 @@ | |||
package utils |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every consumer of utils doesn't need this file, it should be put into a separate package for this module. Suggest connection_manager.go
connectionmgr.go
Outdated
} | ||
|
||
// NewConnectionManager initializes a new ConnectionManager object that can be used | ||
// to manage the life of long lived remote connections such as to a database. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you look at/think about using context for this purpose? https://blog.golang.org/context
connectionmgr.go
Outdated
|
||
// DoInit initializes an object matching the Initializer interface, setting its close | ||
// operation to run when the Connection Manager's Close method is called. | ||
func (cm *ConnectionManager) DoInit(initializer Initializer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is the major functionality of connection manager, I'd think about having the initializer be a field of connection manager and pass it in when calling New for connection manager.
database/db.go
Outdated
@@ -59,16 +61,31 @@ func (d dbLogger) AfterQuery(q *pg.QueryEvent) { | |||
|
|||
// New returns a new DB object which wraps | |||
// a connection to the database specified in config | |||
func New(config DBConfig) DB { | |||
func New(config DBConfig, logger *log.Logger) DB { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer passing logger in as a param of config versus changing the signature of this method
db := pg.Connect(&pg.Options{ | ||
Addr: config.Address, | ||
User: config.User, | ||
Database: config.Database, | ||
Password: config.Password, | ||
}) | ||
if os.Getenv("QUERY_DEBUG") == "ON" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tears of joy to see this removed.
database/db.go
Outdated
} | ||
} | ||
|
||
// Initialize starts up the database and returns the Close function used to gracefully shut it down. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment needs to be updated to be accurate, there is no database started, only an attempt to run migrations(which as a consumer of this code I would like to know is going to happen when I call this code), which I don't think should happen as it prevents someone from using this who wants to do something non standard/consider the database initialized in a different way.. Please keep in mind when adding code to this module that the purpose of utils is to provide flexible general purpose code, not become the abstract class that imposes behavior on consumers.
http.go
Outdated
@@ -162,3 +162,25 @@ func HealthCheckHandler(serviceName string) http.Handler { | |||
w.Write([]byte(fmt.Sprintf("%s service is up.\n", serviceName))) | |||
}) | |||
} | |||
|
|||
// ReqToObject un-marshals a request object body JSON into an object | |||
func ReqToObject(r *http.Request, obj interface{}) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Please follow the convention elsewhere in this code and most of go of spelling things out, I don't see any logic to why we would excludeuest
but not 'ject' .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/object/struct/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest DeserializeJsonRequest
as a more accurate, go'ish name.
http.go
Outdated
|
||
// WriteObjectResponse marshals an object into the response body and sets | ||
// JSON content type headers | ||
func WriteObjectResponse(obj interface{}, w http.ResponseWriter) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest SerializeJsonResponse
for this function name.
logger.go
Outdated
@@ -72,3 +73,18 @@ func NewServiceLogger(serviceName string, level int) ServiceLogger { | |||
} | |||
return logger | |||
} | |||
|
|||
// GetLogger initializes a logging object writing to the requested log file. | |||
func GetLogger(logFile string, serviceName string) *log.Logger { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest the more go'ish name of "MakeLogger"
logger.go
Outdated
@@ -72,3 +73,18 @@ func NewServiceLogger(serviceName string, level int) ServiceLogger { | |||
} | |||
return logger | |||
} | |||
|
|||
// GetLogger initializes a logging object writing to the requested log file. | |||
func GetLogger(logFile string, serviceName string) *log.Logger { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should accept the interface it cares about (io.writer
), not a magic string that is impartially checked for usefulness
validation.go
Outdated
@@ -0,0 +1,43 @@ | |||
package utils |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should also be a separate subpackge
User string | ||
Database string | ||
Password string | ||
Logger *log.Logger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this just be made a Service Logger at this point? Do we fully standardize on it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be a service logger - its at the point now with the last updates Michael made to not have a logger per level and accept human readable log level that its ready for prime time/doesn't have any major usability issues I can see(take that with a grain of salt as I haven't used it in any of the code I've written)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's my proudest go code. That aside, switching between the two is fairly painless and none of the consumer code for the logger will have to change.
server/middleware.go
Outdated
|
||
// JSONLoggingMiddleware wraps an HTTP handler and logs | ||
// the request and de-serialized JSON body. | ||
func JSONLoggingMiddleware(logger *log.Logger) Middleware { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again -- wondering if fully embracing the service logger is better here?
@galxy25 @efabens - big updates based on reviews and conversations. Reset review status and now it could use another look. The changes are a little heavy handed, but after talking with Levi, I decided to fully embrace some of the concepts. The biggest potential breaking change is middleware. I moved the old middleware functions to live with the middleware type turned them into full fledged middleware functions rather than wrapping them. Along with the change, I moved http utils into the server package. I actually wanted to name the package http, but since this would be used with the I refactored the service logger with some ideas based on conversations. I'm hoping the API is a little more flexible. It is now, essentially, a decorated log.Logger, so things like setting log output, prefix, etc. are supported. Along with Connection Manager is broken out to manage Initialization and Close separately and the API for adding managed connections changed to have better names. More (better?) doc strings. The DB struct initialization was refactored based on the golang Naming updates, sub packages. Sorry for the mess! |
cm.ManageClose(ic) | ||
cm.ManageInitialization(ic) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file looks like beautiful go code :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow, high praise! Thanks!
} | ||
|
||
// DB wraps a client for a database. | ||
type DB struct { | ||
Client *pg.DB | ||
Client *pg.DB | ||
Logger *log.Logger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be embedded into this struct and not a public field(I can't think of a use case other code would have for needing access to the database's logger), with the additional upside that methods on this struct can make logging statements via db.Println()
versus db.Logger.Println
https://golang.org/doc/effective_go.html#embedding
https://travix.io/type-embedding-in-go-ba40dd4264df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note though that a service logger satisfies the logger interface so can be passed as the logger value here, and that the underlying Postgres client only accepts a logger interface so won't be able to take advantage of any additional methods we have(though our database package can)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I know log.Logger
is still a concrete type, not an interface :(
@@ -0,0 +1,118 @@ | |||
package connectionmanager |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
connectionmanager feels like an eyesore to me, suggest package connection
if it doesn't conflict with anything else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fought a lot with this name. Happy for something better!
Connection isn't quite accurate since this manages the initialization/shutdown of any number of items as long as they support the initializer/closer interfaces. Indeed, connection is probably even wrong, though it nods to the intent of this managing things like database/elastic search connections. It provides a method of bringing them up and shutting them down as a group, rather than individually. I don't have a good name, so I stuck with connection manager. The file used to be called connectionmgr, but Levi set me straight on short names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
connectiongroup
? Naming is hard, I'll come back to it if I can think of something...
// Closers are queued up internally running only when the ConnectionManager's Close method | ||
// is called. The ConnectionManager runs each Close method in a separate go routine and blocks | ||
// blocks until all are complete. | ||
type ConnectionManager struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe a more idiomatic naming scheme would be to remove Connection
prefix from the names within this package to avoid the stutter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Manager feels too generic to me as a name - but again, I can't seem to name this functionality. Help!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Manager as a whole is generic but it's within the context of connectionmanager: the consumer of the package will be typing connectionmanager.ConnectionManager repeatedly. But it could be connectionmanager.Manager or even connectiongroup.Manager
if sl.logLevel >= levelMap[level] { | ||
sl.Output(2, sl.prefixString(level)+fmt.Sprintln(v...)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we have this I think its safe/time to delete https://github.com/tozny/utils-go/blob/master/logger.go#L10
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logger.go is removed as part of this PR (it was moved here and refactored).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also much better comments than me 👍
Close() | ||
} | ||
|
||
// InitializerCloser is the interface the both initializes and gracefully closes a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that both
|
||
// ManageConnection accepts any number of items matching the InitializerCloser | ||
// interface and manages both an item's initialization and close. | ||
func (cm *ConnectionManager) ManageConnection(initializerClosers ...InitializerCloser) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ManageConnection seems to restart a connection, close -> initialize -> return. Would a more accurate name be Restart
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't...
Though the ordering is a bit confusing, this queues up the close method of the managed item first (which will not run until the connection manager itself has the Close
method called. Once queued, it then adds the items initialization function, which immediately starts a go routine that initializes the item. The order ensure the close function is managed before initialization begins.
The fact that you are asking means this is just a bit too confusing. I'll add additional comments to clarify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh I understand, maybe EnqueueConnection/EnqueueClosers/EnqueueInitializers more accurately describes what they're doing?
w.WriteHeader(http.StatusOK) | ||
w.Write([]byte(fmt.Sprintf("%s service is up.\n", serviceName))) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want these methods to live here(which seems reasonable), I think we should delete the duplicate methods here https://github.com/tozny/utils-go/blob/master/http.go
final = handler(final) | ||
// ExtractBearerToken attempts to extract an Oauth bearer token | ||
// from the provided request, returning extracted token and error (if any) | ||
func ExtractBearerToken(r *http.Request) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please cleanup the now deprecated version of this in https://github.com/tozny/utils-go/blob/master/http.go
// Middleware is a function that decorates an http.Handler | ||
type Middleware func(handler http.Handler) http.Handler | ||
var ( | ||
// SupportedAuthTypes is a whitelist of supported authentication types. Deafult: Bearer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please delete now deprecated values for the below from https://github.com/tozny/utils-go/blob/master/http.go
// JSONLoggingMiddleware converts the utils JsonLogger into Middleware | ||
func JSONLoggingMiddleware(logger *log.Logger) Middleware { | ||
const ( | ||
// ToznyClientIDHeader is the headers key containing a client ID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please delete now deprecated values for the below from https://github.com/tozny/utils-go/blob/master/http.go
|
||
// HandleError is a generic error handler for responding with the given status and error | ||
// using the provided ResponseWriter. | ||
func HandleError(w http.ResponseWriter, statusCode int, response interface{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please delete now deprecated values for the below from https://github.com/tozny/utils-go/blob/master/http.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
http.go is removed as part of this pr and broken up / added to the server subpackage
func New(config DBConfig) DB { | ||
db := pg.Connect(&pg.Options{ | ||
Addr: config.Address, | ||
User: config.User, | ||
Database: config.Database, | ||
Password: config.Password, | ||
}) | ||
if os.Getenv("QUERY_DEBUG") == "ON" { | ||
db.AddQueryHook(dbLogger{logger: log.New(os.Stdout, fmt.Sprintf("%s: %s: ", config.Address, config.Database), log.LstdFlags)}) | ||
if config.EnableLogging { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying to think about how the Service Logger can be integrated into this, so we don't have to specifically enable at the DB level... But everything I can think of is slightly convoluted. Unless we want to restrict the DB logging to a specific level like debug
Features and ideas created with the note service, transferred here.