<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package db encapsulates tsuru connection with MongoDB.
//
// The function Open dials to MongoDB and returns a connection (represented by
// the Storage type). It manages an internal pool of connections, and
// reconnects in case of failures. That means that you should not store
// references to the connection, but always call Open.
package db

import (
	"fmt"
	"github.com/globocom/config"
	"labix.org/v2/mgo"
	"sync"
)

// pool of connections
var conn = make(map[string]*Storage)

// Storage holds the connection with the database.
type Storage struct {
	collections map[string]*mgo.Collection
	session     *mgo.Session
	dbname      string
	mut         sync.RWMutex
}

func open(addr, dbname string) (*Storage, error) {
	key := addr + dbname
	session, err := mgo.Dial(addr)
	if err != nil {
		return nil, err
	}
	storage := &Storage{
		session:     session,
		collections: make(map[string]*mgo.Collection),
		dbname:      dbname,
	}
	conn[key] = storage
	return storage, nil
}

// Open dials to the MongoDB database, and return the connection (represented
// by the type Storage).
//
// addr is a MongoDB connection URI, and dbname is the name of the database.
//
// This function returns a pointer to a Storage, or a non-nil error in case of
// any failure.
func Open(addr, dbname string) (storage *Storage, err error) {
	var ok bool
	defer func() {
		if r := recover(); r != nil {
			storage, err = open(addr, dbname)
		}
	}()
	key := addr + dbname
	if storage, ok = conn[key]; ok {
		storage.session.Ping()
		return storage, nil
	}
	return open(addr, dbname)
}

// Conn reads the tsuru config and calls Open to get a database connection.
//
// Most tsuru packages should probably use this function. Open is intended for
// use when supporting more than one database.
func Conn() (storage *Storage, err error) {
	url, err := config.GetString("database:url")
	if err != nil {
		return nil, fmt.Errorf("configuration error: %s", err)
	}
	dbname, err := config.GetString("database:name")
	if err != nil {
		return nil, fmt.Errorf("configuration error: %s", err)
	}
	return Open(url, dbname)
}

// Close closes the connection.
//
// You can take advantage of defer statement, and write code that look like this:
//
//     st, err := Open("localhost:27017", "tsuru")
//     if err != nil {
//         panic(err)
//     }
//     defer st.Close()
//<commit_msg>db: use a rwmutex to protect access to the pool<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package db encapsulates tsuru connection with MongoDB.
//
// The function Open dials to MongoDB and returns a connection (represented by
// the Storage type). It manages an internal pool of connections, and
// reconnects in case of failures. That means that you should not store
// references to the connection, but always call Open.
package db

import (
	"fmt"
	"github.com/globocom/config"
	"labix.org/v2/mgo"
	"sync"
)

var (
	conn = make(map[string]*Storage) // pool of connections
	mut  sync.RWMutex                // for pool thread safety
)

// Storage holds the connection with the database.
type Storage struct {
	collections map[string]*mgo.Collection
	session     *mgo.Session
	dbname      string
	mut         sync.RWMutex
}

func open(addr, dbname string) (*Storage, error) {
	key := addr + dbname
	session, err := mgo.Dial(addr)
	if err != nil {
		return nil, err
	}
	storage := &Storage{
		session:     session,
		collections: make(map[string]*mgo.Collection),
		dbname:      dbname,
	}
	mut.Lock()
	conn[key] = storage
	mut.Unlock()
	return storage, nil
}

// Open dials to the MongoDB database, and return the connection (represented
// by the type Storage).
//
// addr is a MongoDB connection URI, and dbname is the name of the database.
//
// This function returns a pointer to a Storage, or a non-nil error in case of
// any failure.
func Open(addr, dbname string) (storage *Storage, err error) {
	var ok bool
	defer func() {
		if r := recover(); r != nil {
			storage, err = open(addr, dbname)
		}
	}()
	key := addr + dbname
	mut.RLock()
	if storage, ok = conn[key]; ok {
		mut.RUnlock()
		storage.session.Ping()
		return storage, nil
	}
	mut.RUnlock()
	return open(addr, dbname)
}

// Conn reads the tsuru config and calls Open to get a database connection.
//
// Most tsuru packages should probably use this function. Open is intended for
// use when supporting more than one database.
func Conn() (storage *Storage, err error) {
	url, err := config.GetString("database:url")
	if err != nil {
		return nil, fmt.Errorf("configuration error: %s", err)
	}
	dbname, err := config.GetString("database:name")
	if err != nil {
		return nil, fmt.Errorf("configuration error: %s", err)
	}
	return Open(url, dbname)
}

// Close closes the connection.
//
// You can take advantage of defer statement, and write code that look like this:
//
//     st, err := Open("localhost:27017", "tsuru")
//     if err != nil {
//         panic(err)
//     }
//     defer st.Close()
//
{'fraction_non_alphanumeric': 0.11543598549895058, 'fraction_numerical': 0.003816065636328945, 'mean_word_length': 3.180223285486443, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1144426', 'n_tokens_mistral': 1678, 'n_tokens_neox': 1617, 'n_words': 650}