Skip to content
Snippets Groups Projects
assigner.go 3.68 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright 2015 Manish R Jain <manishrjain@gmail.com>
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * 		http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package uid
    
    import (
    	"bytes"
    	"errors"
    
    	"time"
    
    	"github.com/dgryski/go-farm"
    	"github.com/manishrjain/dgraph/posting"
    	"github.com/manishrjain/dgraph/posting/types"
    	"github.com/manishrjain/dgraph/x"
    )
    
    var log = x.Log("uid")
    
    
    func allocateNew(xid string) (uid uint64, rerr error) {
    
    	for sp := ""; ; sp += " " {
    		txid := xid + sp
    
    		uid = farm.Fingerprint64([]byte(txid)) // Generate from hash.
    
    		log.WithField("txid", txid).WithField("uid", uid).Debug("Generated")
    		if uid == math.MaxUint64 {
    			log.Debug("Hit uint64max while generating fingerprint. Ignoring...")
    			continue
    		}
    
    
    		// Check if this uid has already been allocated.
    
    		key := posting.Key(uid, "_xid_") // uid -> "_xid_" -> xid
    		pl := posting.Get(key)
    
    		if pl.Length() > 0 {
    			// Something already present here.
    			var p types.Posting
    			pl.Get(&p, 0)
    
    			posting.ParseValue(&tmp, p.ValueBytes())
    
    			log.Debug("Found existing xid: [%q]. Continuing...", tmp)
    			continue
    		}
    
    		// Uid hasn't been assigned yet.
    		t := x.Triple{
    			Value:     xid, // not txid
    			Source:    "_assigner_",
    			Timestamp: time.Now(),
    
    		rerr = pl.AddMutation(t, posting.Set)
    		if rerr != nil {
    			x.Err(log, rerr).Error("While adding mutation")
    		}
    		if err := pl.CommitIfDirty(); err != nil {
    			x.Err(log, err).Error("While commiting")
    		}
    		return uid, rerr
    
    	}
    	return 0, errors.New("Some unhandled route lead me here." +
    		" Wake the stupid developer up.")
    }
    
    
    func stringKey(xid string) []byte {
    
    	buf := new(bytes.Buffer)
    	buf.WriteString("_uid_")
    	buf.WriteString("|")
    	buf.WriteString(xid)
    	return buf.Bytes()
    }
    
    
    // TODO: Currently one posting list is modified after another, without
    func GetOrAssign(xid string) (uid uint64, rerr error) {
    	key := stringKey(xid)
    	pl := posting.Get(key)
    
    	if pl.Length() == 0 {
    		// No current id exists. Create one.
    
    		uid, err := allocateNew(xid)
    
    		if err != nil {
    			return 0, err
    		}
    		t := x.Triple{
    			ValueId:   uid,
    			Source:    "_assigner_",
    			Timestamp: time.Now(),
    		}
    		rerr = pl.AddMutation(t, posting.Set)
    		return uid, rerr
    
    	} else if pl.Length() > 1 {
    		log.Fatalf("We shouldn't have more than 1 uid for xid: %v\n", xid)
    
    	} else {
    		// We found one posting.
    		var p types.Posting
    		if ok := pl.Get(&p, 0); !ok {
    			return 0, errors.New("While retrieving entry from posting list")
    		}
    		return p.Uid(), nil
    	}
    	return 0, errors.New("Some unhandled route lead me here." +
    		" Wake the stupid developer up.")
    }
    
    
    func ExternalId(uid uint64) (xid string, rerr error) {
    	key := posting.Key(uid, "_xid_") // uid -> "_xid_" -> xid
    	pl := posting.Get(key)
    
    		return "", errors.New("NO external id")
    
    	if pl.Length() > 1 {
    		log.WithField("uid", uid).Fatal("This shouldn't be happening.")
    
    		return "", errors.New("Multiple external ids for this uid.")
    
    	var p types.Posting
    	if ok := pl.Get(&p, 0); !ok {
    		log.WithField("uid", uid).Error("While retrieving posting")
    		return "", errors.New("While retrieving posting")
    	}
    
    
    	if p.Uid() != math.MaxUint64 {
    		log.WithField("uid", uid).Fatal("Value uid must be MaxUint64.")
    	}
    
    	rerr = posting.ParseValue(&xid, p.ValueBytes())
    
    	return xid, rerr