<commit_before>// Copyright 2016 The Upspin 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 gcp implements upspin.Directory for talking to the Google Cloud Platofrm (GCP).
package gcp

import (
	goPath "path"
	"strings"
	"sync"

	"upspin.io/access"
	"upspin.io/bind"
	"upspin.io/cache"
	"upspin.io/cloud/storage"
	"upspin.io/errors"
	"upspin.io/log"
	"upspin.io/metric"
	"upspin.io/path"
	"upspin.io/upspin"
)

// Configuration options for this package.
const (
	// ConfigProjectID specifies which GCP project to use for talking to GCP.
	// If not specified, "upspin" is used.
	ConfigProjectID = "gcpProjectId"

	// ConfigBucketName specifies which GCS bucket to store data in.
	// If not specified, "g-upspin-directory" is used.
	ConfigBucketName = "gcpBucketName"
)

type directory struct {
	context  upspin.Context
	endpoint upspin.Endpoint

	// TODO: can one address space be configured to talk to multiple GCP backends and/or act as different directory
	// service users, with different keys? If yes, then Configure should configure the caches too.
	// If not, then these should be singletons.
	timeNow        func() upspin.Time // returns the current time.
	cloudClient    storage.Storage    // handle for GCP bucket g-upspin-directory.
	serverName     upspin.UserName    // this server's user name (for talking to Store, etc).
	factotum       upspin.Factotum    // this server's factotum with its keys.
	newStoreClient newStoreClient     // how to create a Store client.
	dirCache       *cache.LRU         // caches <upspin.PathName, upspin.DirEntry>. It is thread safe.
	rootCache      *cache.LRU         // caches <upspin.UserName, root>. It is thread safe.
	dirNegCache    *cache.LRU         // caches the absence of a path <upspin.PathName, nil>. It is thread safe.
}

var _ upspin.Directory = (*directory)(nil)

// gcpDir represents a name for the metric for this directory service.
const gcpDir = "gcpDir"

var (
	zeroLoc          upspin.Location
	errNotConfigured = errors.Str("GCP Directory not configured")

	confLock sync.RWMutex // protects all configuration options and the ref count below.
	refCount uint64
)

// options are optional parameters to almost every inner method of directory for doing some
// optional, non-correctness-related work.
type options struct {
	span *metric.Span
	// Add other things below (for example, some healthz monitoring stats)
}

// verifyMetadata checks that the metadata is minimally valid.
func verifyMetadata(op string, path upspin.PathName, meta upspin.Metadata) error {
	if meta.Sequence < upspin.SeqNotExist {
		return errors.E(op, path, errors.Invalid, errors.Str("invalid sequence number"))
	}
	return nil
}

func newOptsForMetric(op string) (options, *metric.Metric) {
	m := metric.New(gcpDir)
	defer m.Done()
	opts := options{
		span: m.StartSpan(op),
	}
	return opts, m
}

// MakeDirectory implements upspin.Directory.
func (d *directory) MakeDirectory(dirName upspin.PathName) (upspin.Location, error) {
	const op = "MakeDirectory"

	confLock.RLock()
	defer confLock.RUnlock()
	if !d.isConfigured() {
		return zeroLoc, errNotConfigured
	}
	opts, m := newOptsForMetric(op)
	defer m.Done()
<commit_msg>directory/gcp: fix spurious defer in metric<commit_after>// Copyright 2016 The Upspin 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 gcp implements upspin.Directory for talking to the Google Cloud Platofrm (GCP).
package gcp

import (
	goPath "path"
	"strings"
	"sync"

	"upspin.io/access"
	"upspin.io/bind"
	"upspin.io/cache"
	"upspin.io/cloud/storage"
	"upspin.io/errors"
	"upspin.io/log"
	"upspin.io/metric"
	"upspin.io/path"
	"upspin.io/upspin"
)

// Configuration options for this package.
const (
	// ConfigProjectID specifies which GCP project to use for talking to GCP.
	// If not specified, "upspin" is used.
	ConfigProjectID = "gcpProjectId"

	// ConfigBucketName specifies which GCS bucket to store data in.
	// If not specified, "g-upspin-directory" is used.
	ConfigBucketName = "gcpBucketName"
)

type directory struct {
	context  upspin.Context
	endpoint upspin.Endpoint

	// TODO: can one address space be configured to talk to multiple GCP backends and/or act as different directory
	// service users, with different keys? If yes, then Configure should configure the caches too.
	// If not, then these should be singletons.
	timeNow        func() upspin.Time // returns the current time.
	cloudClient    storage.Storage    // handle for GCP bucket g-upspin-directory.
	serverName     upspin.UserName    // this server's user name (for talking to Store, etc).
	factotum       upspin.Factotum    // this server's factotum with its keys.
	newStoreClient newStoreClient     // how to create a Store client.
	dirCache       *cache.LRU         // caches <upspin.PathName, upspin.DirEntry>. It is thread safe.
	rootCache      *cache.LRU         // caches <upspin.UserName, root>. It is thread safe.
	dirNegCache    *cache.LRU         // caches the absence of a path <upspin.PathName, nil>. It is thread safe.
}

var _ upspin.Directory = (*directory)(nil)

// gcpDir represents a name for the metric for this directory service.
const gcpDir = "gcpDir"

var (
	zeroLoc          upspin.Location
	errNotConfigured = errors.Str("GCP Directory not configured")

	confLock sync.RWMutex // protects all configuration options and the ref count below.
	refCount uint64
)

// options are optional parameters to almost every inner method of directory for doing some
// optional, non-correctness-related work.
type options struct {
	span *metric.Span
	// Add other things below (for example, some healthz monitoring stats)
}

// verifyMetadata checks that the metadata is minimally valid.
func verifyMetadata(op string, path upspin.PathName, meta upspin.Metadata) error {
	if meta.Sequence < upspin.SeqNotExist {
		return errors.E(op, path, errors.Invalid, errors.Str("invalid sequence number"))
	}
	return nil
}

func newOptsForMetric(op string) (options, *metric.Metric) {
	m := metric.New(gcpDir)
	opts := options{
		span: m.StartSpan(op),
	}
	return opts, m
}

// MakeDirectory implements upspin.Directory.
func (d *directory) MakeDirectory(dirName upspin.PathName) (upspin.Location, error) {
	const op = "MakeDirectory"

	confLock.RLock()
	defer confLock.RUnlock()
	if !d.isConfigured() {
		return zeroLoc, errNotConfigured
	}
	opts, m := newOptsForMetric(op)
	defer m.Done()

{'fraction_non_alphanumeric': 0.09437136739063934, 'fraction_numerical': 0.0018354236769654328, 'mean_word_length': 3.56953179594689, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 9, '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': '877672', 'n_tokens_mistral': 2051, 'n_tokens_neox': 2006, 'n_words': 717}