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

pace / bricks / 13717956986

06 Mar 2025 02:30PM UTC coverage: 51.823% (-4.8%) from 56.612%
13717956986

push

github

web-flow
Merge pull request #406 from pace/gomod-update

This updates all dependencies to the latest version, excluding

github.com/bsm/redislock
github.com/dave/jennifer

as newer versions lead to unwanted behavior.

54 of 82 new or added lines in 9 files covered. (65.85%)

453 existing lines in 15 files now uncovered.

4889 of 9434 relevant lines covered (51.82%)

20.93 hits per line

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

0.0
/backend/couchdb/health_check.go
1
package couchdb
2

3
import (
4
        "context"
5
        "fmt"
6
        "net/http"
7
        "time"
8

9
        kivik "github.com/go-kivik/kivik/v4"
10
        "github.com/pace/bricks/maintenance/health/servicehealthcheck"
11
)
12

13
// HealthCheck checks the state of the object storage client. It must not be changed
14
// after it was registered as a health check.
15
type HealthCheck struct {
16
        Name   string
17
        Client *kivik.Client
18
        DB     *kivik.DB
19
        Config *Config
20

21
        state servicehealthcheck.ConnectionState
22
}
23

24
var (
25
        healthCheckTimeFormat     = time.RFC3339
26
        healthCheckConcurrentSpan = 10 * time.Second
27
)
28

29
// HealthCheck checks if the object storage client is healthy. If the last result is outdated,
30
// object storage is checked for upload and download,
31
// otherwise returns the old result
32
func (h *HealthCheck) HealthCheck(ctx context.Context) servicehealthcheck.HealthCheckResult {
×
33
        if time.Since(h.state.LastChecked()) <= h.Config.HealthCheckResultTTL {
×
34
                // the last health check is not outdated, an can be reused.
×
35
                return h.state.GetState()
×
36
        }
×
37

38
        checkTime := time.Now()
×
39

×
NEW
40
        var (
×
NEW
41
                doc Doc
×
NEW
42
                err error
×
NEW
43
                row *kivik.Document
×
NEW
44
                rev string
×
NEW
45
        )
×
46

×
47
check:
×
48
        // check if context was canceled
×
49
        select {
×
50
        case <-ctx.Done():
×
51
                h.state.SetErrorState(fmt.Errorf("failed: %v", ctx.Err()))
×
52
                return h.state.GetState()
×
53
        default:
×
54
        }
55

56
        row = h.DB.Get(ctx, h.Config.HealthCheckKey)
×
NEW
57
        if err := row.Err(); err != nil {
×
NEW
58
                if kivik.HTTPStatus(err) == http.StatusNotFound {
×
UNCOV
59
                        goto put
×
60
                }
NEW
61
                h.state.SetErrorState(fmt.Errorf("failed to get: %#v", err))
×
62
                return h.state.GetState()
×
63
        }
NEW
64
        defer row.Close()
×
65

×
66
        // check if document exists
×
NEW
67
        rev, err = row.Rev()
×
NEW
68
        if err != nil {
×
NEW
69
                h.state.SetErrorState(fmt.Errorf("failed to get document revision: %v", err))
×
NEW
70
        }
×
71

NEW
72
        if rev != "" {
×
73
                err = row.ScanDoc(&doc)
×
74
                if err != nil {
×
NEW
75
                        h.state.SetErrorState(fmt.Errorf("failed to get: %v", err))
×
76
                        return h.state.GetState()
×
77
                }
×
78

79
                // check was already perfromed
80
                if wasConcurrentHealthCheck(checkTime, doc.Time) {
×
81
                        goto healthy
×
82
                }
83
        }
84

85
put:
86
        // update document
87
        doc.ID = h.Config.HealthCheckKey
×
88
        doc.Time = time.Now().Format(healthCheckTimeFormat)
×
89
        _, err = h.DB.Put(ctx, h.Config.HealthCheckKey, doc)
×
90
        if err != nil {
×
91
                // not yet created, try to create
×
NEW
92
                if h.Config.DatabaseAutoCreate && kivik.HTTPStatus(err) == http.StatusNotFound {
×
93
                        err := h.Client.CreateDB(ctx, h.Name)
×
94
                        if err != nil {
×
95
                                h.state.SetErrorState(fmt.Errorf("failed to put object: %v", err))
×
96
                                return h.state.GetState()
×
97
                        }
×
98
                        goto put
×
99
                }
100

NEW
101
                if kivik.HTTPStatus(err) == http.StatusConflict {
×
102
                        goto check
×
103
                }
104
                h.state.SetErrorState(fmt.Errorf("failed to put object: %v", err))
×
105
                return h.state.GetState()
×
106
        }
107

108
        // document was uploaded goto check
109
        goto check
×
110

111
healthy:
112
        // If uploading and downloading worked set the Health Check to healthy
113
        h.state.SetHealthy()
×
114
        return h.state.GetState()
×
115
}
116

117
type Doc struct {
118
        ID   string `json:"_id"`
119
        Rev  string `json:"_rev,omitempty"`
120
        Time string `json:"at"`
121
}
122

123
// wasConcurrentHealthCheck checks if the time doesn't match in a certain
124
// time span concurrent request to the objstore may break the assumption
125
// that the value is the same, but in this case it would be acceptable.
126
// Assumption all instances are created equal and one providing evidence
127
// of a good write would be sufficient. See #244
128
func wasConcurrentHealthCheck(checkTime time.Time, observedValue string) bool {
×
129
        t, err := time.Parse(healthCheckTimeFormat, observedValue)
×
130
        if err == nil {
×
131
                allowedStart := checkTime.Add(-healthCheckConcurrentSpan)
×
132
                allowedEnd := checkTime.Add(healthCheckConcurrentSpan)
×
133

×
134
                // timestamp we got from the document is in allowed range
×
135
                // concider it healthy
×
136
                return t.After(allowedStart) && t.Before(allowedEnd)
×
137
        }
×
138

139
        return false
×
140
}
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