예제 #1
0
// Create attempts to create a FileJournal at the given path, creating
// any subdirectories needed by the path.  An implementation of ValueType
// must be given that defines the type of data to be stored.  The
// time units between each data point must also be given.  For a time
// series file that records data points every 60 seconds must have interval
// set to 60.  The meta parameter is a value defined by the application.
func Create(path string, interval int64, factory ValueType, meta []int64) (*FileJournal, error) {
	// Create the base directory, if needed
	dir := filepath.Dir(path)
	dirInfo, err := os.Stat(dir)
	if os.IsNotExist(err) {
		err2 := os.MkdirAll(dir, 0777)
		if err2 != nil {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	} else if !dirInfo.IsDir() {
		return nil, fmt.Errorf("File in the way of directory creation: %s",
			dirInfo.Name())
	}

	if len(meta) > MaxMeta {
		return nil, fmt.Errorf("Length of metadata slice too long")
	}

	// Open a file handle -- truncates existing file, lock new file
	fd, err := os.Create(path)
	if err != nil {
		return nil, err
	}
	err = lock.Exclusive(fd)
	if err != nil {
		fd.Close()
		return nil, err
	}

	// Allocate and fill in our structs
	j := FileJournal{
		header: FileHeader{
			Magic:    Magic,
			Version:  Version,
			Type:     factory.Type(),
			Width:    factory.Width(),
			Interval: interval,
			Epoch:    0,
		},
		fd:       fd,
		readonly: false,
		points:   0,
		factory:  factory,
	}
	copy(j.header.Meta[:], meta)

	// Write out the header
	err = binary.Write(j.fd, binary.LittleEndian, j.header)
	if err != nil {
		return nil, err
	}
	j.fd.Sync()

	return &j, nil
}
예제 #2
0
// Open finds the time series journal referenced by the given path, opens
// the file and returns a FileJournal struct and any possible error.  Try to
// open the underlying file read/write.  If that fails, open the file
// read-only which means Write() calls will return an error.
func Open(path string) (*FileJournal, error) {
	readonly := false
	fd, err := os.OpenFile(path, os.O_RDWR, 0666)
	if os.IsPermission(err) {
		fd, err = os.Open(path)
		readonly = true
	}
	if err != nil {
		return nil, err
	}

	if readonly {
		err = lock.Share(fd)
	} else {
		err = lock.Exclusive(fd)
	}
	if err != nil {
		fd.Close()
		return nil, err
	}

	j := FileJournal{}
	j.fd = fd
	j.readonly = readonly

	err = binary.Read(j.fd, binary.LittleEndian, &(j.header))
	if err != nil {
		// We couldn't fill the header struct -- corrupt file?
		return nil, err
	}

	if j.header.Magic != Magic {
		return nil, fmt.Errorf("Not a journal timeseries: %s", path)
	}

	// Type factory
	j.factory = GetValueType(j.header.Type, j.header.Width)

	// How large are we?
	stat, err := j.fd.Stat()
	if err != nil {
		return nil, err
	}

	if (stat.Size()-HeaderSize)%int64(j.header.Width) != 0 {
		// XXX: How can we recover from a partial Write()?
		return nil, fmt.Errorf("Corrupt or partial data!")
	}

	j.points = (stat.Size() - HeaderSize) / int64(j.header.Width)
	return &j, nil
}