mirror of
https://github.com/tiennm99/keepalive.git
synced 2026-10-11 03:13:31 +00:00
feat: init
using claude
This commit is contained in:
1 parent
764551f30f
commit
1ec8c45c26
7 files changed
+270
-221
No files matched your search
@@ -3,4 +3,6 @@ COUCHBASE_USERNAME=your_couchbase_username
|
||||
COUCHBASE_PASSWORD=your_couchbase_password
|
||||
COUCHBASE_BUCKET_NAME=couchbase-keepalive
|
||||
COUCHBASE_SCOPE_NAME=development
|
||||
COUCHBASE_COLLECTION_NAME=keepalive
|
||||
|
||||
INTERVAL_SECONDS=60
|
||||
TIMEOUT_SECONDS=86400
|
||||
@@ -1,7 +1,3 @@
|
||||
.idea
|
||||
|
||||
|
||||
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
ARG GO_VERSION=1.24.10
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build
|
||||
WORKDIR /src
|
||||
|
||||
RUN --mount=type=cache,target=/go/pkg/mod/ \
|
||||
--mount=type=bind,source=go.sum,target=go.sum \
|
||||
--mount=type=bind,source=go.mod,target=go.mod \
|
||||
go mod download -x
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
RUN --mount=type=cache,target=/go/pkg/mod/ \
|
||||
--mount=type=bind,target=. \
|
||||
CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin/server .
|
||||
|
||||
FROM alpine:latest AS final
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apk \
|
||||
apk --update add \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
&& \
|
||||
update-ca-certificates
|
||||
|
||||
ARG UID=10001
|
||||
RUN adduser \
|
||||
--disabled-password \
|
||||
--gecos "" \
|
||||
--home "/nonexistent" \
|
||||
--shell "/sbin/nologin" \
|
||||
--no-create-home \
|
||||
--uid "${UID}" \
|
||||
appuser
|
||||
USER appuser
|
||||
|
||||
COPY --from=build /bin/server /bin/
|
||||
|
||||
EXPOSE 1999
|
||||
|
||||
ENTRYPOINT [ "/bin/server" ]
|
||||
+1
-22
@@ -1,23 +1,2 @@
|
||||
# couchbase-keepalive
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Archived — moved to [tiennm99/db-keepalive](https://github.com/tiennm99/db-keepalive).**
|
||||
>
|
||||
> All six `*-keepalive` repos were consolidated into a single binary with pluggable adapters. Use `DB_TYPE=couchbase` with the new image:
|
||||
>
|
||||
> ```bash
|
||||
> docker run -d --restart unless-stopped \
|
||||
> -e DB_TYPE=couchbase \
|
||||
> -e COUCHBASE_CONNECTION_STRING='...' -e COUCHBASE_USERNAME=... # ...etc \
|
||||
> ghcr.io/tiennm99/db-keepalive:latest
|
||||
> ```
|
||||
>
|
||||
> The source here is retained for git history. No further changes will land on this repo.
|
||||
|
||||
## Original description
|
||||
|
||||
Lightweight Go utility performing periodic pings to keep Couchbase Capella free clusters active.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0 — see [LICENSE](LICENSE).
|
||||
A lightweight Go utility that performs periodic randomized get/set operations to prevent Couchbase Capella free clusters from entering inactive state.
|
||||
+13
-12
@@ -1,6 +1,8 @@
|
||||
module github.com/tiennm99/couchbase-keepalive
|
||||
|
||||
go 1.24.10
|
||||
go 1.23.12
|
||||
|
||||
toolchain go1.24.10
|
||||
|
||||
require (
|
||||
github.com/couchbase/gocb/v2 v2.11.1
|
||||
@@ -8,7 +10,6 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/couchbase/gocbcore/v10 v10.8.1 // indirect
|
||||
github.com/couchbase/gocbcoreps v0.1.4 // indirect
|
||||
github.com/couchbase/goprotostellar v1.0.2 // indirect
|
||||
@@ -19,17 +20,17 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect
|
||||
google.golang.org/grpc v1.74.2 // indirect
|
||||
google.golang.org/protobuf v1.36.7 // indirect
|
||||
)
|
||||
+26
-30
@@ -2,8 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/couchbase/gocb/v2 v2.11.1 h1:xWDco7Qk/XSvGUjbUWRaXi0V35nsMijJnm4vHXN/rqY=
|
||||
@@ -74,24 +72,24 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ=
|
||||
go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=
|
||||
go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=
|
||||
go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=
|
||||
go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=
|
||||
go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -120,8 +118,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -134,12 +132,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -153,24 +151,22 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a h1:tPE/Kp+x9dMSwUm/uM0JKK0IfdiJkwAbSMSeZBXXJXc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
+227
-112
@@ -2,134 +2,249 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/couchbase/gocb/v2"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Uncomment following line to enable logging
|
||||
// gocb.SetLogger(gocb.VerboseStdioLogger())
|
||||
// Global counter to track number of documents created
|
||||
var documentCounter int = 0
|
||||
var counterMutex sync.Mutex
|
||||
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Println("Warning: .env file not found")
|
||||
}
|
||||
|
||||
// Update this to your cluster details
|
||||
connectionString, isExist := os.LookupEnv("COUCHBASE_CONNECTION_STRING")
|
||||
if !isExist {
|
||||
log.Fatal("Warning: COUCHBASE_CONNECTION_STRING not set!")
|
||||
return
|
||||
}
|
||||
username, isExist := os.LookupEnv("COUCHBASE_USERNAME")
|
||||
if !isExist {
|
||||
log.Fatal("Warning: COUCHBASE_USERNAME not set!")
|
||||
return
|
||||
}
|
||||
password, isExist := os.LookupEnv("COUCHBASE_PASSWORD")
|
||||
if !isExist {
|
||||
log.Fatal("Warning: COUCHBASE_PASSWORD not set!")
|
||||
return
|
||||
}
|
||||
bucketName, isExist := os.LookupEnv("COUCHBASE_BUCKET_NAME")
|
||||
if !isExist {
|
||||
log.Fatal("Warning: COUCHBASE_BUCKET_NAME not set!")
|
||||
return
|
||||
}
|
||||
scopeName, isExist := os.LookupEnv("COUCHBASE_SCOPE_NAME")
|
||||
if !isExist {
|
||||
log.Fatal("Warning: COUCHBASE_SCOPE_NAME not set!")
|
||||
return
|
||||
}
|
||||
collectionName, isExist := os.LookupEnv("COUCHBASE_COLLECTION_NAME")
|
||||
if !isExist {
|
||||
log.Fatal("Warning: COUCHBASE_COLLECTION_NAME not set!")
|
||||
return
|
||||
}
|
||||
|
||||
options := gocb.ClusterOptions{
|
||||
Authenticator: gocb.PasswordAuthenticator{
|
||||
Username: username,
|
||||
Password: password,
|
||||
},
|
||||
}
|
||||
|
||||
// Sets a pre-configured profile called "wan-development" to help avoid latency issues
|
||||
// when accessing Capella from a different Wide Area Network
|
||||
// or Availability Zone (e.g. your laptop).
|
||||
if err := options.ApplyProfile(gocb.ClusterConfigProfileWanDevelopment); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Initialize the Connection
|
||||
cluster, err := gocb.Connect(connectionString, options)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
bucket := cluster.Bucket(bucketName)
|
||||
|
||||
err = bucket.WaitUntilReady(5*time.Second, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Get a reference to the default collection, required for older Couchbase server versions
|
||||
// col := bucket.DefaultCollection()
|
||||
|
||||
col := bucket.Scope(scopeName).Collection(collectionName)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := incrementCounter(col); err != nil {
|
||||
log.Printf("Keepalive increment error: %v", err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
cancel()
|
||||
if err := cluster.Close(nil); err != nil {
|
||||
log.Printf("Error closing cluster: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigCh
|
||||
// KeepaliveDocument represents the JSON document structure for keepalive operations
|
||||
type KeepaliveDocument struct {
|
||||
ID string `json:"id"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Value string `json:"value"`
|
||||
Operation string `json:"operation"`
|
||||
Cluster string `json:"cluster"`
|
||||
}
|
||||
|
||||
func incrementCounter(col *gocb.Collection) error {
|
||||
counterDocId := "counter"
|
||||
docOut, err := col.Get(counterDocId, &gocb.GetOptions{})
|
||||
type Config struct {
|
||||
ClusterURL string
|
||||
Username string
|
||||
Password string
|
||||
BucketName string
|
||||
ScopeName string
|
||||
CollectionName string
|
||||
Interval time.Duration
|
||||
OperationTimeout time.Duration
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Load environment variables from .env file
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
log.Println("No .env file found, using system environment variables")
|
||||
}
|
||||
var current uint64
|
||||
err = docOut.Content(¤t)
|
||||
|
||||
config := loadConfig()
|
||||
|
||||
// Initialize random seed
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Connect to Couchbase cluster
|
||||
cluster, err := connectToCluster(config)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Fatalf("Failed to connect to cluster: %v", err)
|
||||
}
|
||||
current++
|
||||
_, err = col.Upsert(counterDocId, current, &gocb.UpsertOptions{})
|
||||
defer cluster.Close(nil)
|
||||
|
||||
// Test connection to the specified scope
|
||||
err = testScopeConnection(cluster, config)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Fatalf("Failed to connect to scope: %v", err)
|
||||
}
|
||||
log.Printf("Counter : %d\n", current)
|
||||
|
||||
log.Println("Successfully connected to Couchbase cluster")
|
||||
log.Printf("Scope: %s, Collection: %s", config.ScopeName, config.CollectionName)
|
||||
log.Printf("Keeping cluster alive with operations every %v", config.Interval)
|
||||
|
||||
// Start keepalive operations
|
||||
keepAlive(cluster, config)
|
||||
}
|
||||
|
||||
func loadConfig() Config {
|
||||
var config Config
|
||||
|
||||
// Load from environment variables with defaults
|
||||
config.ClusterURL = getEnv("COUCHBASE_CONNECTION_STRING", "localhost")
|
||||
config.Username = getEnv("COUCHBASE_USERNAME", "")
|
||||
config.Password = getEnv("COUCHBASE_PASSWORD", "")
|
||||
config.BucketName = getEnv("COUCHBASE_BUCKET_NAME", "default")
|
||||
config.ScopeName = getEnv("COUCHBASE_SCOPE_NAME", "_default")
|
||||
config.CollectionName = getEnv("COUCHBASE_COLLECTION_NAME", "_default")
|
||||
|
||||
// Parse interval duration from environment variable
|
||||
intervalStr := getEnv("COUCHBASE_INTERVAL", "5m")
|
||||
if interval, err := time.ParseDuration(intervalStr); err == nil {
|
||||
config.Interval = interval
|
||||
} else {
|
||||
log.Printf("Invalid interval '%s', using default 5m", intervalStr)
|
||||
config.Interval = 5 * time.Minute
|
||||
}
|
||||
|
||||
// Parse operation timeout from environment variable
|
||||
timeoutStr := getEnv("COUCHBASE_OPERATION_TIMEOUT", "10s")
|
||||
if timeout, err := time.ParseDuration(timeoutStr); err == nil {
|
||||
config.OperationTimeout = timeout
|
||||
} else {
|
||||
log.Printf("Invalid operation timeout '%s', using default 10s", timeoutStr)
|
||||
config.OperationTimeout = 10 * time.Second
|
||||
}
|
||||
|
||||
// Validate required parameters
|
||||
if config.Username == "" || config.Password == "" {
|
||||
log.Fatal("COUCHBASE_USERNAME and COUCHBASE_PASSWORD are required")
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func connectToCluster(config Config) (*gocb.Cluster, error) {
|
||||
cluster, err := gocb.Connect(config.ClusterURL, gocb.ClusterOptions{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create cluster connection: %w", err)
|
||||
}
|
||||
|
||||
// Get the bucket
|
||||
bucket := cluster.Bucket(config.BucketName)
|
||||
|
||||
return cluster, bucket, nil
|
||||
}
|
||||
|
||||
func testScopeConnection(cluster *gocb.Cluster, config Config) error {
|
||||
bucket := cluster.Bucket(config.BucketName)
|
||||
scope := bucket.Scope(config.ScopeName)
|
||||
collection := scope.Collection(config.CollectionName)
|
||||
|
||||
// Test the connection to the specific scope and collection with configured timeout
|
||||
err := collection.Ping(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to ping scope '%s', collection '%s': %w",
|
||||
config.ScopeName, config.CollectionName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func keepAlive(cluster *gocb.Cluster, config Config) {
|
||||
ticker := time.NewTicker(config.Interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err := performRandomOperation(cluster, config)
|
||||
if err != nil {
|
||||
log.Printf("Error performing keepalive operation: %v", err)
|
||||
} else {
|
||||
log.Println("Successfully performed keepalive operation")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func performRandomOperation(cluster *gocb.Cluster, config Config) error {
|
||||
bucket := cluster.Bucket(config.BucketName)
|
||||
scope := bucket.Scope(config.ScopeName)
|
||||
collection := scope.Collection(config.CollectionName)
|
||||
|
||||
// 50% chance to perform GET or SET operation
|
||||
if rand.Intn(2) == 0 {
|
||||
return performGetOperation(collection, config)
|
||||
} else {
|
||||
return performSetOperation(collection, config)
|
||||
}
|
||||
}
|
||||
|
||||
func getCurrentHourSeconds() string {
|
||||
now := time.Now()
|
||||
hourSeconds := now.Hour()*3600 + now.Minute()*60 + now.Second()
|
||||
return fmt.Sprintf("%d", hourSeconds)
|
||||
}
|
||||
|
||||
// incrementCounter increases the document counter and returns the new value
|
||||
func incrementCounter() int {
|
||||
counterMutex.Lock()
|
||||
defer counterMutex.Unlock()
|
||||
documentCounter++
|
||||
return documentCounter
|
||||
}
|
||||
|
||||
// getCurrentCounter returns the current document counter value
|
||||
func getCurrentCounter() int {
|
||||
counterMutex.Lock()
|
||||
defer counterMutex.Unlock()
|
||||
return documentCounter
|
||||
}
|
||||
|
||||
func performGetOperation(collection *gocb.Collection, config Config) error {
|
||||
// Use the current counter value to get the latest document
|
||||
currentCounter := getCurrentCounter()
|
||||
docID := fmt.Sprintf("%d", currentCounter)
|
||||
|
||||
// Create a document struct for the Get result
|
||||
var doc KeepaliveDocument
|
||||
|
||||
// Get the document with configured timeout
|
||||
_, err := collection.Get(docID, &gocb.GetOptions{
|
||||
Timeout: config.OperationTimeout,
|
||||
})
|
||||
|
||||
// It's okay if the document doesn't exist, we're just testing the operation
|
||||
if err != nil && err != gocb.ErrDocumentNotFound {
|
||||
return fmt.Errorf("get operation failed: %w", err)
|
||||
}
|
||||
|
||||
// Try to decode the document to verify JSON structure
|
||||
// We don't need the actual data, just testing the operation
|
||||
_ = doc
|
||||
|
||||
log.Printf("Successfully retrieved document with counter: %d", currentCounter)
|
||||
return nil
|
||||
}
|
||||
|
||||
func performSetOperation(collection *gocb.Collection, config Config) error {
|
||||
// Use the incremented counter value as document ID
|
||||
newCounter := incrementCounter()
|
||||
docID := fmt.Sprintf("%d", newCounter)
|
||||
|
||||
// Create a structured JSON document
|
||||
document := KeepaliveDocument{
|
||||
ID: docID,
|
||||
Timestamp: time.Now().Unix(),
|
||||
Value: fmt.Sprintf("keepalive-value-%d", newCounter),
|
||||
Operation: "keepalive",
|
||||
Cluster: config.ClusterURL,
|
||||
}
|
||||
|
||||
// Set the document with a short TTL (1 hour) and configured timeout
|
||||
_, err := collection.Insert(docID, document, &gocb.InsertOptions{
|
||||
Expiry: time.Hour,
|
||||
Timeout: config.OperationTimeout,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("set operation failed: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Successfully created document with counter: %d", newCounter)
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user