func FlushServiceProfileCache(ctx context.Context, id uuid.UUID) error {
	key := GetRedisKey(ServiceProfileKeyTempl, id)
	err := RedisClient().Del(ctx, key).Err()
	if err != nil {
		return errors.Wrap(err, "delete error")
	}
	if err := FlushGatewayMetaCacheForServiceProfile(ctx, DB(), id); err != nil {
		return errors.Wrap(err, "flush gateway meta cache for service-profile id error")
	}
	return nil
}
FlushServiceProfileCache deletes a cached service-profile.
package storage
import (
	"bytes"
	"context"
	"encoding/gob"
	"time"
	"github.com/go-redis/redis/v8"
	"github.com/gofrs/uuid"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/brocaar/chirpstack-network-server/v3/internal/logging"
)
const (
	ServiceProfileKeyTempl = "lora:ns:sp:%s"
)
type RatePolicy string
const (
	Drop RatePolicy = "Drop"
	Mark RatePolicy = "Mark"
)
type ServiceProfile struct {
	CreatedAt              time.Time  `db:"created_at"`
	UpdatedAt              time.Time  `db:"updated_at"`
	ID                     uuid.UUID  `db:"service_profile_id"`
	ULRate                 int        `db:"ul_rate"`
	ULBucketSize           int        `db:"ul_bucket_size"`
	ULRatePolicy           RatePolicy `db:"ul_rate_policy"`
	DLRate                 int        `db:"dl_rate"`
	DLBucketSize           int        `db:"dl_bucket_size"`
	DLRatePolicy           RatePolicy `db:"dl_rate_policy"`
	AddGWMetadata          bool       `db:"add_gw_metadata"`
	DevStatusReqFreq       int        `db:"dev_status_req_freq"`  
	ReportDevStatusBattery bool       `db:"report_dev_status_battery"`
	ReportDevStatusMargin  bool       `db:"report_dev_status_margin"`
	DRMin                  int        `db:"dr_min"`
	DRMax                  int        `db:"dr_max"`
	ChannelMask            []byte     `db:"channel_mask"`
	PRAllowed              bool       `db:"pr_allowed"`
	HRAllowed              bool       `db:"hr_allowed"`
	RAAllowed              bool       `db:"ra_allowed"`
	NwkGeoLoc              bool       `db:"nwk_geo_loc"`
	TargetPER              int        `db:"target_per"`  
	MinGWDiversity         int        `db:"min_gw_diversity"`
	GwsPrivate             bool       `db:"gws_private"`
}
func CreateServiceProfile(ctx context.Context, db sqlx.Execer, sp *ServiceProfile) error {
	now := time.Now()
	if sp.ID == uuid.Nil {
		var err error
		sp.ID, err = uuid.NewV4()
		if err != nil {
			return errors.Wrap(err, "new uuid v4 error")
		}
	}
	sp.CreatedAt = now
	sp.UpdatedAt = now
	_, err := db.Exec(`
		insert into service_profile (
			created_at,
			updated_at,
			service_profile_id,
			ul_rate,
			ul_bucket_size,
			ul_rate_policy,
			dl_rate,
			dl_bucket_size,
			dl_rate_policy,
			add_gw_metadata,
			dev_status_req_freq,
			report_dev_status_battery,
			report_dev_status_margin,
			dr_min,
			dr_max,
			channel_mask,
			pr_allowed,
			hr_allowed,
			ra_allowed,
			nwk_geo_loc,
			target_per,
			min_gw_diversity,
			gws_private
		) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)`,
		sp.CreatedAt,
		sp.UpdatedAt,
		sp.ID,
		sp.ULRate,
		sp.ULBucketSize,
		sp.ULRatePolicy,
		sp.DLRate,
		sp.DLBucketSize,
		sp.DLRatePolicy,
		sp.AddGWMetadata,
		sp.DevStatusReqFreq,
		sp.ReportDevStatusBattery,
		sp.ReportDevStatusMargin,
		sp.DRMin,
		sp.DRMax,
		sp.ChannelMask,
		sp.PRAllowed,
		sp.HRAllowed,
		sp.RAAllowed,
		sp.NwkGeoLoc,
		sp.TargetPER,
		sp.MinGWDiversity,
		sp.GwsPrivate,
	)
	if err != nil {
		return handlePSQLError(err, "insert error")
	}
	log.WithFields(log.Fields{
		"id":     sp.ID,
		"ctx_id": ctx.Value(logging.ContextIDKey),
	}).Info("service-profile created")
	return nil
}
func CreateServiceProfileCache(ctx context.Context, sp ServiceProfile) error {
	key := GetRedisKey(ServiceProfileKeyTempl, sp.ID)
	var buf bytes.Buffer
	if err := gob.NewEncoder(&buf).Encode(sp); err != nil {
		return errors.Wrap(err, "gob encode service-profile error")
	}
	err := RedisClient().Set(ctx, key, buf.Bytes(), deviceSessionTTL).Err()
	if err != nil {
		return errors.Wrap(err, "set service-profile error")
	}
	return nil
}
func GetServiceProfileCache(ctx context.Context, id uuid.UUID) (ServiceProfile, error) {
	var sp ServiceProfile
	key := GetRedisKey(ServiceProfileKeyTempl, id)
	val, err := RedisClient().Get(ctx, key).Bytes()
	if err != nil {
		if err == redis.Nil {
			return sp, ErrDoesNotExist
		}
		return sp, errors.Wrap(err, "get error")
	}
	err = gob.NewDecoder(bytes.NewReader(val)).Decode(&sp)
	if err != nil {
		return sp, errors.Wrap(err, "gob decode error")
	}
	return sp, nil
}