• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Permify / permify / 10584694062

27 Aug 2024 07:35PM UTC coverage: 79.671%. Remained the same
10584694062

Pull #1525

github

tolgaOzen
refactor: improve error handling and optimize variable declaration
Pull Request #1525: refactor: improve error handling and optimize variable declaration

7 of 7 new or added lines in 3 files covered. (100.0%)

4 existing lines in 2 files now uncovered.

8050 of 10104 relevant lines covered (79.67%)

114.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.61
/internal/storage/postgres/utils/common.go
1
package utils
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "log/slog"
8
        "math"
9
        "strings"
10
        "time"
11

12
        "go.opentelemetry.io/otel/codes"
13
        "golang.org/x/exp/rand"
14

15
        "go.opentelemetry.io/otel/trace"
16

17
        "github.com/Masterminds/squirrel"
18

19
        base "github.com/Permify/permify/pkg/pb/base/v1"
20
)
21

22
const (
23
        BulkEntityFilterTemplate = `
24
WITH filtered_entities AS (
25
    SELECT DISTINCT ON (entity_id) id, entity_id
26
    FROM (
27
        SELECT id, entity_id, tenant_id, entity_type, created_tx_id, expired_tx_id
28
        FROM relation_tuples
29
        WHERE tenant_id = '%s' AND entity_type = '%s' AND %s AND %s
30
        UNION ALL
31
        SELECT id, entity_id, tenant_id, entity_type, created_tx_id, expired_tx_id
32
        FROM attributes
33
        WHERE tenant_id = '%s' AND entity_type = '%s' AND %s AND %s
34
    ) AS entities
35
)
36
SELECT entity_id
37
FROM filtered_entities
38
`
39

40
        TransactionTemplate       = `INSERT INTO transactions (tenant_id) VALUES ($1) RETURNING id`
41
        InsertTenantTemplate      = `INSERT INTO tenants (id, name) VALUES ($1, $2) RETURNING created_at`
42
        DeleteTenantTemplate      = `DELETE FROM tenants WHERE id = $1 RETURNING name, created_at`
43
        DeleteAllByTenantTemplate = `DELETE FROM %s WHERE tenant_id = $1`
44
)
45

46
// SnapshotQuery adds conditions to a SELECT query for checking transaction visibility based on created and expired transaction IDs.
47
// The query checks if transactions are visible in a snapshot associated with the provided value.
48
func SnapshotQuery(sl squirrel.SelectBuilder, value uint64) squirrel.SelectBuilder {
1✔
49
        // Convert the value to a string once to reduce redundant calls to fmt.Sprintf.
1✔
50
        valStr := fmt.Sprintf("'%v'::xid8", value)
1✔
51

1✔
52
        // Create a subquery for the snapshot associated with the provided value.
1✔
53
        snapshotQuery := fmt.Sprintf("(select snapshot from transactions where id = %s)", valStr)
1✔
54

1✔
55
        // Create an expression to check if a transaction with a specific created_tx_id is visible in the snapshot.
1✔
56
        visibilityExpr := squirrel.Expr(fmt.Sprintf("pg_visible_in_snapshot(created_tx_id, %s) = true", snapshotQuery))
1✔
57
        // Create an expression to check if the created_tx_id is equal to the provided value.
1✔
58
        createdExpr := squirrel.Expr(fmt.Sprintf("created_tx_id = %s", valStr))
1✔
59
        // Use OR condition for the created expressions.
1✔
60
        createdWhere := squirrel.Or{visibilityExpr, createdExpr}
1✔
61

1✔
62
        // Create an expression to check if a transaction with a specific expired_tx_id is not visible in the snapshot.
1✔
63
        expiredVisibilityExpr := squirrel.Expr(fmt.Sprintf("pg_visible_in_snapshot(expired_tx_id, %s) = false", snapshotQuery))
1✔
64
        // Create an expression to check if the expired_tx_id is equal to zero.
1✔
65
        expiredZeroExpr := squirrel.Expr("expired_tx_id = '0'::xid8")
1✔
66
        // Create an expression to check if the expired_tx_id is not equal to the provided value.
1✔
67
        expiredNotExpr := squirrel.Expr(fmt.Sprintf("expired_tx_id <> %s", valStr))
1✔
68
        // Use AND condition for the expired expressions, checking both visibility and non-equality with value.
1✔
69
        expiredWhere := squirrel.And{squirrel.Or{expiredVisibilityExpr, expiredZeroExpr}, expiredNotExpr}
1✔
70

1✔
71
        // Add the created and expired conditions to the SELECT query.
1✔
72
        return sl.Where(createdWhere).Where(expiredWhere)
1✔
73
}
1✔
74

75
// snapshotQuery function generates two strings representing conditions to be applied in a SQL query to filter data based on visibility of transactions.
76
func snapshotQuery(value uint64) (string, string) {
1✔
77
        // Convert the provided value into a string format suitable for our SQL query, formatted as a transaction ID.
1✔
78
        valStr := fmt.Sprintf("'%v'::xid8", value)
1✔
79

1✔
80
        // Create a subquery that fetches the snapshot associated with the transaction ID.
1✔
81
        snapshotQ := fmt.Sprintf("(SELECT snapshot FROM transactions WHERE id = %s)", valStr)
1✔
82

1✔
83
        // Create an expression that checks whether a transaction (represented by 'created_tx_id') is visible in the snapshot.
1✔
84
        visibilityExpr := fmt.Sprintf("pg_visible_in_snapshot(created_tx_id, %s) = true", snapshotQ)
1✔
85
        // Create an expression that checks if the 'created_tx_id' is the same as our transaction ID.
1✔
86
        createdExpr := fmt.Sprintf("created_tx_id = %s", valStr)
1✔
87
        // Combine these expressions to form a condition. A row will satisfy this condition if either of the expressions are true.
1✔
88
        createdWhere := fmt.Sprintf("(%s OR %s)", visibilityExpr, createdExpr)
1✔
89

1✔
90
        // Create an expression that checks whether a transaction (represented by 'expired_tx_id') is not visible in the snapshot.
1✔
91
        expiredVisibilityExpr := fmt.Sprintf("pg_visible_in_snapshot(expired_tx_id, %s) = false", snapshotQ)
1✔
92
        // Create an expression that checks if the 'expired_tx_id' is zero. This handles cases where the transaction hasn't expired.
1✔
93
        expiredZeroExpr := "expired_tx_id = '0'::xid8"
1✔
94
        // Create an expression that checks if the 'expired_tx_id' is not the same as our transaction ID.
1✔
95
        expiredNotExpr := fmt.Sprintf("expired_tx_id <> %s", valStr)
1✔
96
        // Combine these expressions to form a condition. A row will satisfy this condition if the first set of expressions are true (either the transaction hasn't expired, or if it has, it's not visible in the snapshot) and the second expression is also true (the 'expired_tx_id' is not the same as our transaction ID).
1✔
97
        expiredWhere := fmt.Sprintf("(%s AND %s)", fmt.Sprintf("(%s OR %s)", expiredVisibilityExpr, expiredZeroExpr), expiredNotExpr)
1✔
98

1✔
99
        // Return the conditions for both 'created' and 'expired' transactions. These can be used in a WHERE clause of a SQL query to filter results.
1✔
100
        return createdWhere, expiredWhere
1✔
101
}
1✔
102

103
// BulkEntityFilterQuery -
104
func BulkEntityFilterQuery(tenantID, entityType string, snap uint64) string {
1✔
105
        createdWhere, expiredWhere := snapshotQuery(snap)
1✔
106
        return fmt.Sprintf(BulkEntityFilterTemplate, tenantID, entityType, createdWhere, expiredWhere, tenantID, entityType, createdWhere, expiredWhere)
1✔
107
}
1✔
108

109
// GenerateGCQuery generates a Squirrel DELETE query builder for garbage collection.
110
// It constructs a query to delete expired records from the specified table
111
// based on the provided value, which represents a transaction ID.
112
func GenerateGCQuery(table string, value uint64) squirrel.DeleteBuilder {
1✔
113
        // Convert the provided value into a string format suitable for our SQL query, formatted as a transaction ID.
1✔
114
        valStr := fmt.Sprintf("'%v'::xid8", value)
1✔
115

1✔
116
        // Create a Squirrel DELETE builder for the specified table.
1✔
117
        deleteBuilder := squirrel.Delete(table)
1✔
118

1✔
119
        // Create an expression to check if 'expired_tx_id' is not equal to '0' (not expired).
1✔
120
        expiredZeroExpr := squirrel.Expr("expired_tx_id <> '0'::xid8")
1✔
121

1✔
122
        // Create an expression to check if 'expired_tx_id' is less than the provided value (before the cutoff).
1✔
123
        beforeExpr := squirrel.Expr(fmt.Sprintf("expired_tx_id < %s", valStr))
1✔
124

1✔
125
        // Add the WHERE clauses to the DELETE query builder to filter and delete expired data.
1✔
126
        return deleteBuilder.Where(expiredZeroExpr).Where(beforeExpr)
1✔
127
}
1✔
128

129
// HandleError records an error in the given span, logs the error, and returns a standardized error.
130
// This function is used for consistent error handling across different parts of the application.
131
func HandleError(ctx context.Context, span trace.Span, err error, errorCode base.ErrorCode) error {
×
132
        // Check if the error is context-related
×
133
        if IsContextRelatedError(ctx, err) {
×
134
                slog.DebugContext(ctx, "A context-related error occurred",
×
135
                        slog.String("error", err.Error()))
×
136
                return errors.New(base.ErrorCode_ERROR_CODE_CANCELLED.String())
×
137
        }
×
138

139
        // Check if the error is serialization-related
140
        if IsSerializationRelatedError(err) {
×
141
                slog.DebugContext(ctx, "A serialization-related error occurred",
×
142
                        slog.String("error", err.Error()))
×
143
                return errors.New(base.ErrorCode_ERROR_CODE_SERIALIZATION.String())
×
144
        }
×
145

146
        // For all other types of errors, log them at the error level and record them in the span
147
        slog.ErrorContext(ctx, "An operational error occurred",
×
148
                slog.Any("error", err))
×
149
        span.RecordError(err)
×
150
        span.SetStatus(codes.Error, err.Error())
×
151

×
152
        // Return a new error with the standard error code provided
×
153
        return errors.New(errorCode.String())
×
154
}
155

156
// IsContextRelatedError checks if the error is due to context cancellation, deadline exceedance, or closed connection
157
func IsContextRelatedError(ctx context.Context, err error) bool {
×
158
        if errors.Is(ctx.Err(), context.Canceled) || errors.Is(ctx.Err(), context.DeadlineExceeded) {
×
159
                return true
×
160
        }
×
161
        if errors.Is(err, context.Canceled) ||
×
162
                errors.Is(err, context.DeadlineExceeded) ||
×
163
                strings.Contains(err.Error(), "conn closed") {
×
164
                return true
×
165
        }
×
166
        return false
×
167
}
168

169
// IsSerializationRelatedError checks if the error is a serialization failure, typically in database transactions.
170
func IsSerializationRelatedError(err error) bool {
×
171
        if strings.Contains(err.Error(), "could not serialize") ||
×
172
                strings.Contains(err.Error(), "duplicate key value") {
×
173
                return true
×
174
        }
×
175
        return false
×
176
}
177

178
// WaitWithBackoff implements an exponential backoff strategy with jitter for retries.
179
// It waits for a calculated duration or until the context is cancelled, whichever comes first.
180
func WaitWithBackoff(ctx context.Context, tenantID string, retries int) {
×
181
        backoff := time.Duration(math.Min(float64(20*time.Millisecond)*math.Pow(2, float64(retries)), float64(1*time.Second)))
×
182
        jitter := time.Duration(rand.Float64() * float64(backoff) * 0.5)
×
183
        nextBackoff := backoff + jitter
×
184
        slog.WarnContext(ctx, "waiting before retry", slog.String("tenant_id", tenantID), slog.Int64("backoff_duration", nextBackoff.Milliseconds()))
×
185
        select {
×
186
        case <-time.After(nextBackoff):
×
187
        case <-ctx.Done():
×
188
        }
189
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc