// GetIDRecord returns the ID record for a given idname. It also // will error if the ID has exceeded its max permitted value. func (mkv *MongoKV) GetIDRecord(idname string) (IDRecord, error) { var result IDRecord err := mkv.DBs["id"].FindOne(bson.M{"key": idname}, &result) if err != nil { return result, util.NewWebError(500, "STAT-102", fmt.Sprintf("Incremented ID '%s' but didn't find it!", idname)).Add("mongoerr", err.Error()) } if result.Value >= result.Max { return result, util.NewWebError(500, "STAT-103", fmt.Sprintf("ID max %d exceeded for IDName %s. Unable to create new ID.", result.Max, idname)) } return result, nil }
// GetIDFor is like GetNextID, except that it takes a "hash" value; // if the hash exists, it always returns the same key value for it. // if the hash doesn't exist, then it generates a new key and stores it // under that hash. func (mkv *MongoKV) GetIDFor(idname string, hash string) (string, error) { var result map[string]interface{} hashsel := bson.M{"hash": hash, "idname": idname} if err := mkv.DBs["id"].FindOne(hashsel, &result); err == nil { switch id := result["id"].(type) { case int: // if it's an int, we need to prefix it appropriately idrec, err := mkv.GetIDRecord(idname) if err != nil { return "", err } sid := fmt.Sprintf("%s%d", idrec.Prefix, id) return sid, nil case string: // if it's a string, we have what we need return id, nil } } // if we get here, the hash wasn't found id, err := mkv.GetNextID(idname) if err != nil { return "", err } // now we have id, store it under the hash newhash := bson.M{"hash": hash, "idname": idname, "id": id} err = mkv.DBs["id"].Insert(newhash) if err != nil { return id, util.NewWebError(500, "STAT-103", fmt.Sprintf("Got ID %d from '%s' for '%s' but couldn't store it!", id, idname, hash)).Add("mongoerr", err.Error()) } return id, nil }
// GetNextID increments the ID value for the given idname // and returns an appropriately-prefixed ID for that name. func (mkv *MongoKV) GetNextID(idname string) (string, error) { selector := bson.M{"key": idname} update := bson.M{"$inc": bson.M{"value": 1}} err := mkv.DBs["id"].Update(selector, update) if err != nil { return "", util.NewWebError(404, "STAT-101", fmt.Sprintf("No id found with name '%s'", idname)).Add("mongoerr", err.Error()) } idrec, err := mkv.GetIDRecord(idname) if err != nil { return "", err } id := fmt.Sprintf("%s%d", idrec.Prefix, idrec.Value) return id, nil }
func (ss *StaticServer) PostKey(rw http.ResponseWriter, req *http.Request) { key := bone.GetValue(req, "key") body, err := ioutil.ReadAll(req.Body) if err != nil { util.WriteWebError(rw, util.NewWebError(http.StatusBadRequest, "STAT-211", err.Error())) } b := string(body) value := getJSON(b) ss.setS3RootIfPresent(key, value) if err := ss.mkv.Upsert(key, value); err != nil { util.WriteError(rw, "STAT-212", err) return } util.WriteJSON(rw, value) }
func (mkv *MongoKV) Reconnect(m *mongoHelper.M) error { // we want to make sure that our writes get to disk m.Sess.SetSafe(&mgo.Safe{}) m.Sess.SetMode(mgo.Strong, false) index1 := mgo.Index{ Key: []string{"key"}, Unique: true, DropDups: true, Background: true, // See notes. Sparse: true, } err := m.Coll.EnsureIndex(index1) if err != nil { return util.NewWebError(http.StatusInternalServerError, "STAT-101", fmt.Sprintf("Error creating index1")).Add("original", err.Error()) } return nil }
// Connect will connect to the MongoDB server(s) specified in the URL. // It also supports connections where ssl is enabled. func (m *M) Connect() error { var ( err error useSSL bool sess *mgo.Session dialInfo *mgo.DialInfo ) // Do this to allow mgo.ParseURL to succeed (fails on ssl param otherwise). if strings.Contains(m.Url, "ssl=true") { m.Url = strings.Replace(m.Url, "ssl=true", "", -1) useSSL = true } dialInfo, err = mgo.ParseURL(m.Url) dialInfo.Timeout = time.Second * 10 // Use a dialer with TLS if secure connection desired. if useSSL { dialInfo.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) { return tls.Dial("tcp", addr.String(), &tls.Config{}) } } // We always dial with info to support both SSL and non-SSL connections. sess, err = mgo.DialWithInfo(dialInfo) if err != nil { return util.NewWebError(http.StatusInternalServerError, "MH-100", "Can't connect to mongo").Add("original", err.Error()) } if m.Log != nil { m.Log.Println("Connecting to mongo db " + m.DbName + ", collection " + m.CollName) } coll := sess.DB(m.DbName).C(m.CollName) m.Sess, m.Coll = sess, coll return m.Reconn.Reconnect(m) }