The Dial method of the mgo package establishes a connection to the cluster of MongoDB servers, which returns an mgo.Session object. You can manage all CRUD operations using the Session object, which manages the pool of connections to the MongoDB servers. A connection pool is a cache of database connections, so connections can be reused when new connections to the database are required. When you develop web applications, using a single global Session object for all CRUD operations is a really bad practice.
A recommended process for managing the Session object in web applications is shown here: 1. Obtain a Session object using the Dial method.
2. Create a new Session object during the lifetime of an individual HTTP request by using the New, Copy, or Clone methods on the Session object obtained from the Dial method. This approach enables the Session object to use the connection pool appropriately.
3. Use the newly obtained Session object to perform all CRUD operations during the lifetime of an HTTP request.
The New method creates a new Session with the same parameters as the original Session. The Copy method works just like New, but the copied Session preserves the exact authentication information from the original Session. The Clone method works just like Copy, but it also reuses the same socket (connection to the server) as the original Session.
Listing 8-18 is an example HTTP server that uses a copied Session object during the lifetime of an HTTP request. In this example, a struct type is created to hold the Session object for easily managing database operations from the application handlers.
Listing 8-18. HTTP Server Using a New Session for Each HTTP Request package main import ( "encoding/json" "log" "net/http" "github.com/gorilla/mux" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" )
ChApter 8 ■ perSISteNCe WIth MONgODB type ( Category struct { Id bson.ObjectId `bson:"_id,omitempty"` Name string Description string } DataStore struct { session *mgo.Session } ) //Close mgo.Session
func (d *DataStore) Close() { d.session.Close()
}
//Returns a collection from the database.
func (d *DataStore) C(name string) *mgo.Collection { return d.session.DB("taskdb").C(name)
}
//Create a new DataStore object for each HTTP request func NewDataStore() *DataStore {
ds := &DataStore{ session: session.Copy(), } return ds } //Insert a record
func PostCategory(w http.ResponseWriter, r *http.Request) { var category Category
// Decode the incoming Category json
err := json.NewDecoder(r.Body).Decode(&category) if err != nil { panic(err) } ds := NewDataStore() defer ds.Close()
//Getting the mgo.Collection c := ds.C("categories") //Insert record err = c.Insert(&category) if err != nil { panic(err) } w.WriteHeader(http.StatusCreated) }
ChApter 8 ■ perSISteNCe WIth MONgODB
//Read all records
func GetCategories(w http.ResponseWriter, r *http.Request) { var categories []Category
ds := NewDataStore() defer ds.Close()
//Getting the mgo.Collection c := ds.C("categories") iter := c.Find(nil).Iter() result := Category{} for iter.Next(&result) {
categories = append(categories, result) } w.Header().Set("Content-Type", "application/json") j, err := json.Marshal(categories) if err != nil { panic(err) } w.WriteHeader(http.StatusOK) w.Write(j) } func main() { var err error
session, err = mgo.Dial("localhost") if err != nil { panic(err) } r := mux.NewRouter() r.HandleFunc("/api/categories", GetCategories).Methods("GET") r.HandleFunc("/api/categories", PostCategory).Methods("POST") server := &http.Server{ Addr: ":8080", Handler: r, } log.Println("Listening...") server.ListenAndServe() }
A DataStore struct type is defined to easily manage mgo.Session. Two methods are added to the DataStore type: Close and C. The Close method releases the resources of the newly created Session object by calling the Close method of the Session object. This method is invoked by using the defer function after the lifetime of an HTTP request. The C method returns the Collection object with the given name:
type DataStore struct { session *mgo.Session }
ChApter 8 ■ perSISteNCe WIth MONgODB
//Close mgo.Session
func (d *DataStore) Close() { d.session.Close()
}
//Returns a collection from the database.
func (d *DataStore) C(name string) *mgo.Collection { return d.session.DB("taskdb").C(name)
}
The NewDataStore function creates a new DataStore object by providing a new Session object using the Copy method of the Session obtained from the Dial method:
func NewDataStore() *DataStore { ds := &DataStore{
session: session.Copy(),
}
return ds }
The NewDataStore function is called to create a DataStore object for providing a copied Session object being used in the lifetime of an HTTP request. For each handler for a route, a new Session object is used through the DataStore type. In short, using a global Session object is not a good practice; it is recommended to use a copied Session object for the lifetime of each HTTP request. This approach allows you to having multiple Session objects if required.
Summary
When working with web applications, it is important that you persist application data into persistence storage. In this chapter, you learned how to persist data into MongoDB using the mgo package, which is a MongoDB driver for Go.
MongoDB is an open-source document database that provides high performance, high availability, and automatic scaling. MongoDB is the most popular NoSQL database; it has been widely used by modern web applications. The mgo driver provides a simple API that follows the Go idioms.
You can add indexes to fields of MongoDB collections, which provide high performance on read operations. When developing web applications in Go, using a global mgo.Session object is not a good practice. The right approach for managing Session is to create a new Session object by calling Copy, Clone, or New on the obtained Session object, which was obtained by the Dial method of the mgo package.