Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resty metrics #3

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3d7d71c
Do not lose calling context in the logger
peterbroadhurst Feb 8, 2024
bec7b66
Add direct exec option
peterbroadhurst Feb 12, 2024
e736cc1
Do not log an error if a JSON field is missing
nguyer Feb 22, 2024
1a76e02
[ffapi] Support for Text and Binary Streams
onelapahead Feb 13, 2024
a7aceb2
io.ReadCloser to prescribe return type
onelapahead Feb 13, 2024
6cd3816
comments and enum
onelapahead Feb 13, 2024
5e2ff09
headers
onelapahead Feb 13, 2024
729ca04
lint
onelapahead Feb 13, 2024
e26c9d2
avoiding passing route
onelapahead Feb 13, 2024
2d5deff
struct to avoid awkward optional string param for streams
onelapahead Feb 13, 2024
1edbce4
fix bugs with handlerOutput
onelapahead Feb 13, 2024
e3f3b81
simplifying openapi generation options for streams and other edge cas…
onelapahead Feb 13, 2024
1c74e1a
Fixes
onelapahead Feb 13, 2024
0868da9
set description if none provided
onelapahead Feb 13, 2024
5582e08
handler tests for text and binary streams
onelapahead Feb 26, 2024
a707576
openapi ut
onelapahead Feb 26, 2024
26af1df
apiserver test
onelapahead Feb 26, 2024
eb93fd6
Merge pull request #127 from kaleido-io/feature/stream-openapi-support
nguyer Feb 26, 2024
04b5589
Merge pull request #130 from kaleido-io/json-error-log
peterbroadhurst Feb 28, 2024
901bd3a
Merge branch 'main' into log-ctx
nguyer Feb 29, 2024
a54fd73
Merge pull request #126 from hyperledger/log-ctx
nguyer Feb 29, 2024
d1b60ec
[fftls] Allow for In-Memory or Inlined TLS Certificates
onelapahead Mar 11, 2024
cb44f29
fix linter
onelapahead Mar 11, 2024
7116b31
fix coverage
onelapahead Mar 11, 2024
4d97472
linter
onelapahead Mar 11, 2024
0a735f3
comments
onelapahead Mar 11, 2024
b6bcbb4
Merge pull request #132 from kaleido-io/feature/in-memory-tls-certs
nguyer Mar 11, 2024
c437cb1
Do not add alias prefix to filter fields that are already qualified
awrichar Apr 4, 2024
d2ba7f7
Merge pull request #134 from kaleido-io/filter-alias
peterbroadhurst Apr 30, 2024
c05b693
Allow customizing the name of the required "id" column
awrichar May 7, 2024
cf40dfe
Add AfterLoad CRUD hook
awrichar May 7, 2024
d9ac6df
Add comment on AfterLoad usage
awrichar May 9, 2024
dab0027
Merge pull request #137 from kaleido-io/after-load
awrichar May 9, 2024
c656563
DB-side UPSERT optimiziation support
peterbroadhurst May 13, 2024
b64d486
Work through UTs on PSQL optimization
peterbroadhurst May 16, 2024
e1a20e0
Fix copyright headers
peterbroadhurst May 16, 2024
5c087ce
Add contraint column
peterbroadhurst May 16, 2024
38236e1
Syntax
peterbroadhurst May 16, 2024
aac7f50
Add missing config descriptions
awrichar May 23, 2024
d4e905e
Merge pull request #138 from hyperledger/db-conflict
peterbroadhurst May 24, 2024
30b1a08
Merge pull request #140 from kaleido-io/tls-config
peterbroadhurst May 24, 2024
593e20d
add support for http metrics to ffresty also generic global onError/o…
Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mocks/authmocks/plugin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mocks/crudmocks/crud.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mocks/dbmigratemocks/driver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mocks/httpservermocks/go_http_server.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mocks/wsservermocks/protocol.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mocks/wsservermocks/web_socket_server.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/config/cobracmd.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2023 Kaleido, Inc.
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
Expand Down Expand Up @@ -27,7 +27,7 @@ func ShowConfigCommand(initConf func() error) *cobra.Command {
Use: "showconfig",
Aliases: []string{"showconf"},
Short: "List out the configuration options",
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
if err := initConf(); err != nil {
return err
}
Expand Down
104 changes: 88 additions & 16 deletions pkg/dbsql/crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package dbsql
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
"strings"
Expand Down Expand Up @@ -50,6 +51,7 @@ const (
UpsertOptimizationSkip UpsertOptimization = iota
UpsertOptimizationNew
UpsertOptimizationExisting
UpsertOptimizationDB // only supported if the DB layer support ON CONFLICT semantics
)

type GetOption int
Expand Down Expand Up @@ -129,6 +131,7 @@ type CrudBase[T Resource] struct {
TimesDisabled bool // no management of the time columns
PatchDisabled bool // allows non-pointer fields, but prevents UpdateSparse function
ImmutableColumns []string
IDField string // override default ID field
NameField string // If supporting name semantics
QueryFactory ffapi.QueryFactory // Must be set when name is set
DefaultSort func() []interface{} // optionally override the default sort - array of *ffapi.SortField or string
Expand All @@ -144,6 +147,7 @@ type CrudBase[T Resource] struct {
ReadTableAlias string
ReadOnlyColumns []string
ReadQueryModifier QueryModifier
AfterLoad func(ctx context.Context, inst T) error // perform final validation/formatting after an instance is loaded from db
}

func (c *CrudBase[T]) Scoped(scope sq.Eq) CRUD[T] {
Expand All @@ -159,6 +163,13 @@ func (c *CrudBase[T]) TableAlias() string {
return c.Table
}

func (c *CrudBase[T]) GetIDField() string {
if c.IDField != "" {
return c.IDField
}
return ColumnID
}

func (c *CrudBase[T]) GetQueryFactory() ffapi.QueryFactory {
return c.QueryFactory
}
Expand Down Expand Up @@ -212,7 +223,7 @@ func (c *CrudBase[T]) Validate() {
ptrs := map[string]interface{}{}
fieldMap := map[string]bool{
// Mandatory column checks
ColumnID: false,
c.GetIDField(): false,
}
if !c.TimesDisabled {
fieldMap[ColumnCreated] = false
Expand Down Expand Up @@ -260,25 +271,30 @@ func (c *CrudBase[T]) idFilter(id string) sq.Eq {
filter = sq.Eq{}
}
if c.ReadTableAlias != "" {
filter[fmt.Sprintf("%s.id", c.ReadTableAlias)] = id
filter[fmt.Sprintf("%s.%s", c.ReadTableAlias, c.GetIDField())] = id
} else {
filter["id"] = id
}
return filter
}

func (c *CrudBase[T]) isImmutable(col string) bool {
for _, immutable := range append(c.ImmutableColumns, c.GetIDField(), ColumnCreated, ColumnUpdated, c.DB.sequenceColumn) {
if col == immutable {
return true
}
}
return false
}

func (c *CrudBase[T]) buildUpdateList(_ context.Context, update sq.UpdateBuilder, inst T, includeNil bool) sq.UpdateBuilder {
colLoop:
for _, col := range c.Columns {
for _, immutable := range append(c.ImmutableColumns, ColumnID, ColumnCreated, ColumnUpdated, c.DB.sequenceColumn) {
if col == immutable {
continue colLoop
if !c.isImmutable(col) {
value := c.getFieldValue(inst, col)
if includeNil || !isNil(value) {
update = update.Set(col, value)
}
}
value := c.getFieldValue(inst, col)
if includeNil || !isNil(value) {
update = update.Set(col, value)
}
}
if !c.TimesDisabled {
update = update.Set(ColumnUpdated, fftypes.Now())
Expand Down Expand Up @@ -323,9 +339,8 @@ func (c *CrudBase[T]) getFieldValue(inst T, col string) interface{} {
return val
}

func (c *CrudBase[T]) setInsertTimestamps(inst T) {
func (c *CrudBase[T]) setInsertTimestamps(inst T, now *fftypes.FFTime) {
if !c.TimesDisabled {
now := fftypes.Now()
inst.SetCreated(now)
inst.SetUpdated(now)
}
Expand All @@ -344,7 +359,7 @@ func (c *CrudBase[T]) attemptInsert(ctx context.Context, tx *TXWrapper, inst T,
}
}

c.setInsertTimestamps(inst)
c.setInsertTimestamps(inst, fftypes.Now())
insert := sq.Insert(c.Table).Columns(c.Columns...)
values := make([]interface{}, len(c.Columns))
for i, col := range c.Columns {
Expand Down Expand Up @@ -374,11 +389,18 @@ func (c *CrudBase[T]) Upsert(ctx context.Context, inst T, optimization UpsertOpt
// The expectation is that the optimization will hit almost all of the time,
// as only recovery paths require us to go down the un-optimized route.
optimized := false
if optimization == UpsertOptimizationNew {
switch {
case optimization == UpsertOptimizationDB && c.DB.features.DBOptimizedUpsertBuilder != nil:
optimized = true // the DB does the work here, so any failure is a straight failure
created, err = c.dbOptimizedUpsert(ctx, tx, inst)
if err != nil {
return false, err
}
case optimization == UpsertOptimizationNew:
opErr := c.attemptInsert(ctx, tx, inst, true /* we want a failure here we can progress past */)
optimized = opErr == nil
created = optimized
} else if optimization == UpsertOptimizationExisting {
default: // UpsertOptimizationExisting, or fallback if DB optimization requested
rowsAffected, opErr := c.updateFromInstance(ctx, tx, inst, true /* full replace */)
optimized = opErr == nil && rowsAffected == 1
}
Expand Down Expand Up @@ -417,6 +439,47 @@ func (c *CrudBase[T]) Upsert(ctx context.Context, inst T, optimization UpsertOpt
return created, c.DB.CommitTx(ctx, tx, autoCommit)
}

func (c *CrudBase[T]) dbOptimizedUpsert(ctx context.Context, tx *TXWrapper, inst T) (created bool, err error) {

// Caller responsible for checking this is available before driving this path
optimizedInsertBuilder := c.DB.provider.Features().DBOptimizedUpsertBuilder

if c.IDValidator != nil {
if err := c.IDValidator(ctx, inst.GetID()); err != nil {
return false, err
}
}
now := fftypes.Now()
c.setInsertTimestamps(inst, now)

values := make(map[string]driver.Value)
updateCols := make([]string, 0, len(c.Columns))
for _, col := range c.Columns {
values[col] = c.getFieldValue(inst, col)
if !c.isImmutable(col) {
updateCols = append(updateCols, col)
}
}
var rows *sql.Rows
query, err := optimizedInsertBuilder(ctx, c.Table, c.GetIDField(), c.Columns, updateCols, ColumnCreated, values)
if err == nil {
rows, _, err = c.DB.RunAsQueryTx(ctx, c.Table, tx, query.PlaceholderFormat(c.DB.features.PlaceholderFormat))
}
if err != nil {
return false, err
}
defer rows.Close()
if rows.Next() {
var createTime fftypes.FFTime
if err = rows.Scan(&createTime); err != nil {
return false, i18n.NewError(ctx, i18n.MsgDBReadInsertTSFailed, err)
}
created = !createTime.Time().Before(*now.Time())
}
return created, nil

}

func (c *CrudBase[T]) InsertMany(ctx context.Context, instances []T, allowPartialSuccess bool, hooks ...PostCompletionHook) (err error) {

ctx, tx, autoCommit, err := c.DB.BeginOrUseTx(ctx)
Expand All @@ -427,7 +490,7 @@ func (c *CrudBase[T]) InsertMany(ctx context.Context, instances []T, allowPartia
if c.DB.Features().MultiRowInsert {
insert := sq.Insert(c.Table).Columns(c.Columns...)
for _, inst := range instances {
c.setInsertTimestamps(inst)
c.setInsertTimestamps(inst, fftypes.Now())
values := make([]interface{}, len(c.Columns))
for i, col := range c.Columns {
values[i] = c.getFieldValue(inst, col)
Expand Down Expand Up @@ -626,6 +689,9 @@ func (c *CrudBase[T]) GetByID(ctx context.Context, id string, getOpts ...GetOpti
if err != nil {
return c.NilValue(), err
}
if c.AfterLoad != nil {
return inst, c.AfterLoad(ctx, inst)
}
return inst, nil
}

Expand Down Expand Up @@ -728,6 +794,12 @@ func (c *CrudBase[T]) getManyScoped(ctx context.Context, tableFrom string, fi *f
if err != nil {
return nil, nil, err
}
if c.AfterLoad != nil {
err = c.AfterLoad(ctx, inst)
if err != nil {
return nil, nil, err
}
}
instances = append(instances, inst)
}
log.L(ctx).Debugf("SQL<- GetMany(%s): %d", c.Table, len(instances))
Expand Down
Loading
Loading