Merge remote-tracking branch 'origin/main' into ds-apiserver-with-configs

This commit is contained in:
Ryan McKinley
2025-07-10 14:29:04 -07:00
7 changed files with 124 additions and 13 deletions
@@ -62,13 +62,13 @@ For more details on contact points, including how to test them and enable notifi
| Key | Description |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Title | The issue title. Supports templating. Max length is 255 characters. |
| Summary | The issue title. Supports templating. Max length is 255 characters. |
| Description | The description of the issue. Depending on version of the API, it can be a text, Markdown, or JSON (v3 API only). Maximum size of the field is 32kb.<br>**Note:** JSON is not limited by the client, and if it exceeds the size, the API is likely to reject the request. |
| Labels | Custom labels can be added to organize and filter issues created in Jira. Supports templating, allowing dynamic label generation based on alert data. |
| Priority | The priority level of the issue (e.g., `Low`, `Medium`, `High`, `Critical`). <br>Ensure that the priority value matches the available options for your Jira instance. You can customize priority levels in Jira [here](https://support.atlassian.com/jira-cloud-administration/docs/configure-priorities/). |
| Resolve Transition | The transition name to move the issue to a resolved state when an alert is resolved. Ensure that the value matches a valid transition available in your Jira workflow for the specified issue type. If this field is empty, the issue will not be transitioned to Done. |
| Reopen Transition | The transition name to move the issue back to an open state when an alert reoccurs. Ensure that the value matches a valid transition available in your Jira workflow for the specified issue type. If this field is empty, the issue will not be reopened. |
| Reopen Duration | The time duration (in minutes) to control whether to reopen an issue that was closed within this duration or create a new one. If not specified, the most recent issue that matches the deduplication key will be updated and reopened (if reopen transition is specified). |
| Ignored Resolution | Specify a resolution status that should be ignored when searching for existing issues. For example, issues with this resolution will not be reopened or updated by subsequent alerts. |
| "Won't fix" Transition | Specify a resolution status that should be ignored when searching for existing issues. For example, issues with this resolution will not be reopened or updated by subsequent alerts. |
| Deduplication Key Field | Custom field to store the deduplication key. Must be a text field. <br> If not specified, the deduplication key is added to labels in the format of `ALERT(hash sum)`. See [Jira documentation](https://support.atlassian.com/jira-cloud-administration/docs/create-a-custom-field/) for how to create custom fields. |
| Fields | Allows to configure custom fields of Jira issue. The field name should be of the format like `customfield_10001`. |
@@ -32,9 +32,21 @@ func NewOpenFGAServer(cfg setting.ZanzanaServerSettings, store storage.OpenFGADa
opts := []server.OpenFGAServiceV1Option{
server.WithDatastore(store),
server.WithLogger(zlogger.New(logger)),
server.WithCheckQueryCacheEnabled(cfg.CheckQueryCache),
server.WithCheckQueryCacheTTL(cfg.CheckQueryCacheTTL),
server.WithCheckCacheLimit(cfg.CacheSettings.CheckCacheLimit),
server.WithCacheControllerEnabled(cfg.CacheSettings.CacheControllerEnabled),
server.WithCacheControllerTTL(cfg.CacheSettings.CacheControllerTTL),
server.WithCheckQueryCacheEnabled(cfg.CacheSettings.CheckQueryCacheEnabled),
server.WithCheckQueryCacheTTL(cfg.CacheSettings.CheckQueryCacheTTL),
server.WithCheckIteratorCacheEnabled(cfg.CacheSettings.CheckIteratorCacheEnabled),
server.WithCheckIteratorCacheMaxResults(cfg.CacheSettings.CheckIteratorCacheMaxResults),
server.WithCheckIteratorCacheTTL(cfg.CacheSettings.CheckIteratorCacheTTL),
server.WithListObjectsMaxResults(cfg.ListObjectsMaxResults),
server.WithListObjectsIteratorCacheEnabled(cfg.CacheSettings.ListObjectsIteratorCacheEnabled),
server.WithListObjectsIteratorCacheMaxResults(cfg.CacheSettings.ListObjectsIteratorCacheMaxResults),
server.WithListObjectsIteratorCacheTTL(cfg.CacheSettings.ListObjectsIteratorCacheTTL),
server.WithSharedIteratorEnabled(cfg.CacheSettings.SharedIteratorEnabled),
server.WithSharedIteratorLimit(cfg.CacheSettings.SharedIteratorLimit),
server.WithSharedIteratorTTL(cfg.CacheSettings.SharedIteratorTTL),
server.WithListObjectsDeadline(cfg.ListObjectsDeadline),
}
+1 -1
View File
@@ -62,7 +62,7 @@ func NewServer(cfg setting.ZanzanaServerSettings, openfga OpenFGAServer, logger
storesMU: &sync.Mutex{},
stores: make(map[string]storeInfo),
cfg: cfg,
cache: localcache.New(cfg.CheckQueryCacheTTL, cacheCleanInterval),
cache: localcache.New(cfg.CacheSettings.CheckQueryCacheTTL, cacheCleanInterval),
logger: logger,
tracer: tracer,
}
@@ -178,7 +178,7 @@ func (s *Server) listObjects(ctx context.Context, req *openfgav1.ListObjectsRequ
fn = s.streamedListObjects
}
if s.cfg.CheckQueryCache {
if s.cfg.CacheSettings.CheckQueryCacheEnabled {
return s.listObjectCached(ctx, req, fn)
}
+66 -6
View File
@@ -33,10 +33,8 @@ type ZanzanaServerSettings struct {
// OpenFGA http server address which allows to connect with fga cli.
// Can only be used in dev mode.
OpenFGAHttpAddr string
// Enable cache for Check() requests
CheckQueryCache bool
// TTL for cached requests. Default is 10 seconds.
CheckQueryCacheTTL time.Duration
// Cache settings
CacheSettings OpenFgaCacheSettings
// Max number of results returned by ListObjects() query. Default is 1000.
ListObjectsMaxResults uint32
// Deadline for the ListObjects() query. Default is 3 seconds.
@@ -50,6 +48,54 @@ type ZanzanaServerSettings struct {
AllowInsecure bool
}
// Parameters to configure OpenFGA cache.
type OpenFgaCacheSettings struct {
// Number of items that will be kept in the in-memory cache used to resolve Check queries.
// If CheckQueryCacheEnabled or CheckIteratorCacheEnabled, this is the size limit of the cache
CheckCacheLimit uint32
// When enabled, cache controller will verify whether check subproblem cache and check
// iterator cache needs to be invalidated when there is a check or list objects API request.
// The invalidation determination is based on whether there are recent write or deletes for
// the store. This feature allows a larger check-query-cache-ttl and check-iterator-cache-ttl
// at the expense of additional datastore queries for recent writes and deletes.
CacheControllerEnabled bool
// Specifies how frequently the cache controller checks for Writes occurring. While the
// cache controller result is cached, the server will not read the datastore to check
// whether subproblem cache and iterator cache needs to be invalidated.
CacheControllerTTL time.Duration
// Enables in-memory caching of Check subproblems. For example, if you have a relation
// `define viewer: owner or editor`, and the query is `Check(user:anne, viewer, doc:1)`,
// we'll evaluate the `owner` relation and the `editor` relation and cache both results:
// `(user:anne, viewer, doc:1) -> allowed=true` and `(user:anne, owner, doc:1) -> allowed=true`.
CheckQueryCacheEnabled bool
// Time that items will be kept in the cache of Check subproblems
CheckQueryCacheTTL time.Duration
// Enables in-memory caching of database iterators. Each iterator is the result of a database
// query, for example, usersets related to a specific object, or objects related to a specific
// user, up to a certain number of tuples per iterator
CheckIteratorCacheEnabled bool
// Number of tuples that will be stored for each database iterator
CheckIteratorCacheMaxResults uint32
// Time that items will be kept in the cache of database iterators
CheckIteratorCacheTTL time.Duration
// Enables in-memory caching of database iterators for ListObjects. Each iterator is the result
// of a database query, for example, usersets related to a specific object, or objects related
// to a specific user, up to a certain number of tuples per iterator
ListObjectsIteratorCacheEnabled bool
// Number of tuples that will be stored for each ListObjects database iterator
ListObjectsIteratorCacheMaxResults uint32
// Time that items will be kept in the cache of ListObjects database iterators
ListObjectsIteratorCacheTTL time.Duration
// Enables sharing of datastore iterators with different consumers. Each iterator is the result
// of a database query, for example usersets related to a specific object, or objects related
// to a specific user, up to a certain number of tuples per iterator
SharedIteratorEnabled bool
// Limit of the number of iterators that can be shared when shared iterator is enabled
SharedIteratorLimit uint32
// Time that shared iterators will be kept in the cache
SharedIteratorTTL time.Duration
}
func (cfg *Cfg) readZanzanaSettings() {
zc := ZanzanaClientSettings{}
clientSec := cfg.SectionWithEnvOverrides("zanzana.client")
@@ -73,13 +119,27 @@ func (cfg *Cfg) readZanzanaSettings() {
serverSec := cfg.SectionWithEnvOverrides("zanzana.server")
zs.OpenFGAHttpAddr = serverSec.Key("http_addr").MustString("127.0.0.1:8080")
zs.CheckQueryCache = serverSec.Key("check_query_cache").MustBool(true)
zs.CheckQueryCacheTTL = serverSec.Key("check_query_cache_ttl").MustDuration(10 * time.Second)
zs.ListObjectsDeadline = serverSec.Key("list_objects_deadline").MustDuration(3 * time.Second)
zs.ListObjectsMaxResults = uint32(serverSec.Key("list_objects_max_results").MustUint(1000))
zs.UseStreamedListObjects = serverSec.Key("use_streamed_list_objects").MustBool(false)
zs.SigningKeysURL = serverSec.Key("signing_keys_url").MustString("")
zs.AllowInsecure = serverSec.Key("allow_insecure").MustBool(false)
// Cache settings
zs.CacheSettings.CheckCacheLimit = uint32(serverSec.Key("check_cache_limit").MustUint(10000))
zs.CacheSettings.CacheControllerEnabled = serverSec.Key("cache_controller_enabled").MustBool(false)
zs.CacheSettings.CacheControllerTTL = serverSec.Key("cache_controller_ttl").MustDuration(10 * time.Second)
zs.CacheSettings.CheckQueryCacheEnabled = serverSec.Key("check_query_cache_enabled").MustBool(true)
zs.CacheSettings.CheckQueryCacheTTL = serverSec.Key("check_query_cache_ttl").MustDuration(10 * time.Second)
zs.CacheSettings.CheckIteratorCacheEnabled = serverSec.Key("check_iterator_cache_enabled").MustBool(false)
zs.CacheSettings.CheckIteratorCacheMaxResults = uint32(serverSec.Key("check_iterator_cache_max_results").MustUint(1000))
zs.CacheSettings.CheckIteratorCacheTTL = serverSec.Key("check_iterator_cache_ttl").MustDuration(10 * time.Second)
zs.CacheSettings.ListObjectsIteratorCacheEnabled = serverSec.Key("list_objects_iterator_cache_enabled").MustBool(false)
zs.CacheSettings.ListObjectsIteratorCacheMaxResults = uint32(serverSec.Key("list_objects_iterator_cache_max_results").MustUint(1000))
zs.CacheSettings.ListObjectsIteratorCacheTTL = serverSec.Key("list_objects_iterator_cache_ttl").MustDuration(10 * time.Second)
zs.CacheSettings.SharedIteratorEnabled = serverSec.Key("shared_iterator_enabled").MustBool(false)
zs.CacheSettings.SharedIteratorLimit = uint32(serverSec.Key("shared_iterator_limit").MustUint(1000))
zs.CacheSettings.SharedIteratorTTL = serverSec.Key("shared_iterator_ttl").MustDuration(10 * time.Second)
cfg.ZanzanaServer = zs
}
+3 -1
View File
@@ -124,7 +124,7 @@ func newFrame(schema *arrow.Schema) *data.Frame {
func newField(f arrow.Field) *data.Field {
switch f.Type.ID() {
case arrow.STRING:
case arrow.STRING, arrow.STRING_VIEW:
return newDataField[string](f)
case arrow.FLOAT32:
return newDataField[float32](f)
@@ -239,6 +239,8 @@ func copyData(field *data.Field, col arrow.Array) error {
return err
}
}
case arrow.STRING_VIEW:
copyBasic[string](field, array.NewStringViewData(colData))
case arrow.STRING:
copyBasic[string](field, array.NewStringData(colData))
case arrow.UINT8:
+37
View File
@@ -469,6 +469,43 @@ func TestCopyData_Float64(t *testing.T) {
assert.Equal(t, float64(3.3), *field.CopyAt(2).(*float64))
}
func TestCopyData_StringView(t *testing.T) {
// Non-nullable StringView
field := data.NewField("field", nil, []string{})
builder := array.NewStringViewBuilder(memory.DefaultAllocator)
builder.Append("apple")
builder.Append("banana")
builder.Append("cherry")
arr := builder.NewArray()
defer arr.Release()
svArr := array.NewStringViewData(arr.Data())
defer svArr.Release()
err := copyData(field, svArr)
assert.NoError(t, err)
assert.Equal(t, "apple", field.CopyAt(0))
assert.Equal(t, "banana", field.CopyAt(1))
assert.Equal(t, "cherry", field.CopyAt(2))
// Nullable StringView
field = data.NewField("field", nil, []*string{})
builder = array.NewStringViewBuilder(memory.DefaultAllocator)
builder.Append("dog")
builder.AppendNull()
builder.Append("cat")
arr2 := builder.NewArray()
defer arr2.Release()
svArr2 := array.NewStringViewData(arr2.Data())
defer svArr2.Release()
err = copyData(field, svArr2)
assert.NoError(t, err)
assert.Equal(t, "dog", *(field.CopyAt(0).(*string)))
assert.Nil(t, field.CopyAt(1))
assert.Equal(t, "cat", *(field.CopyAt(2).(*string)))
}
func TestCustomMetadata(t *testing.T) {
schema := arrow.NewSchema([]arrow.Field{
{