Skip to content

Commit 7be8b1e

Browse files
authored
feat(nroci): Client Do Request Implementation (#1106)
* feat: Do request + example * refactor: Do request logic * feat: Add fields to Datastore segment * refactor: Wrap Client request to ensure safety from different types * fix: Operation not always definitive based on statement * test: Added tests
1 parent ed775f1 commit 7be8b1e

File tree

5 files changed

+353
-77
lines changed

5 files changed

+353
-77
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"os"
6+
"time"
7+
8+
"github.com/newrelic/go-agent/v3/integrations/nroci"
9+
"github.com/newrelic/go-agent/v3/newrelic"
10+
"github.com/oracle/nosql-go-sdk/nosqldb"
11+
)
12+
13+
func main() {
14+
app, err := newrelic.NewApplication(
15+
newrelic.ConfigAppName("Basic NOSQLOCI App"),
16+
newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")),
17+
newrelic.ConfigDebugLogger(os.Stdout),
18+
)
19+
if err != nil {
20+
panic(err)
21+
}
22+
app.WaitForConnection(10 * time.Second) // for short lived processes in apps
23+
defer app.Shutdown(10 * time.Second)
24+
25+
cfg := nroci.NRDefaultConfig()
26+
27+
clientWrapper, err := nroci.NRCreateClient(cfg)
28+
if err != nil {
29+
panic(err)
30+
}
31+
defer clientWrapper.Client.Close()
32+
33+
txn := app.StartTransaction("OCI NoSQL Transaction")
34+
35+
ctx := newrelic.NewContext(context.Background(), txn)
36+
37+
_, err = nroci.NRDoTableRequest(clientWrapper, ctx, &nosqldb.TableRequest{})
38+
if err != nil {
39+
panic(err)
40+
}
41+
txn.End()
42+
}

v3/integrations/nroci/nroci_nosql.go

Lines changed: 0 additions & 76 deletions
This file was deleted.

v3/integrations/nroci/nroci_nosql_test.go

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// Copyright 2020 New Relic Corporation. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package nroci
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"net/url"
9+
"time"
10+
11+
"github.com/newrelic/go-agent/v3/newrelic"
12+
"github.com/oracle/nosql-go-sdk/nosqldb"
13+
)
14+
15+
func init() {
16+
//add more here
17+
}
18+
19+
type OCIClient interface {
20+
AddReplica(req *nosqldb.AddReplicaRequest) (*nosqldb.TableResult, error)
21+
Close() error
22+
Delete(req *nosqldb.DeleteRequest) (*nosqldb.DeleteResult, error)
23+
DoSystemRequest(req *nosqldb.SystemRequest) (*nosqldb.SystemResult, error)
24+
DoSystemRequestAndWait(statement string, timeout time.Duration, pollInterval time.Duration) (*nosqldb.SystemResult, error)
25+
DoTableRequest(req *nosqldb.TableRequest) (*nosqldb.TableResult, error)
26+
DoTableRequestAndWait(req *nosqldb.TableRequest, timeout time.Duration, pollInterval time.Duration) (*nosqldb.TableResult, error)
27+
DropReplica(req *nosqldb.DropReplicaRequest) (*nosqldb.TableResult, error)
28+
EnableRateLimiting(enable bool, usePercent float64)
29+
30+
Get(req *nosqldb.GetRequest) (*nosqldb.GetResult, error)
31+
GetIndexes(req *nosqldb.GetIndexesRequest) (*nosqldb.GetIndexesResult, error)
32+
GetQueryVersion() int16
33+
GetReplicaStats(req *nosqldb.ReplicaStatsRequest) (*nosqldb.ReplicaStatsResult, error)
34+
GetSerialVersion() int16
35+
GetServerSerialVersion() int
36+
GetSystemStatus(req *nosqldb.SystemStatusRequest) (*nosqldb.SystemResult, error)
37+
GetTable(req *nosqldb.GetTableRequest) (*nosqldb.TableResult, error)
38+
GetTableUsage(req *nosqldb.TableUsageRequest) (*nosqldb.TableUsageResult, error)
39+
40+
ListNamespaces() ([]string, error)
41+
ListRoles() ([]string, error)
42+
ListTables(req *nosqldb.ListTablesRequest) (*nosqldb.ListTablesResult, error)
43+
ListUsers() ([]nosqldb.UserInfo, error)
44+
45+
MultiDelete(req *nosqldb.MultiDeleteRequest) (*nosqldb.MultiDeleteResult, error)
46+
Prepare(req *nosqldb.PrepareRequest) (*nosqldb.PrepareResult, error)
47+
Put(req *nosqldb.PutRequest) (*nosqldb.PutResult, error)
48+
Query(req *nosqldb.QueryRequest) (*nosqldb.QueryResult, error)
49+
ResetRateLimiters(tableName string)
50+
SetQueryVersion(qVer int16)
51+
SetSerialVersion(sVer int16)
52+
VerifyConnection() error
53+
WriteMultiple(req *nosqldb.WriteMultipleRequest) (*nosqldb.WriteMultipleResult, error)
54+
}
55+
56+
type ConfigWrapper struct {
57+
Config *nosqldb.Config
58+
}
59+
60+
type ClientWrapper struct {
61+
Client OCIClient
62+
Config *nosqldb.Config
63+
}
64+
65+
type ClientRequestWrapper[R any] struct {
66+
ClientRequest R
67+
}
68+
type ClientResponseWrapper[T any] struct {
69+
ClientResponse T
70+
}
71+
72+
func NRDefaultConfig() *ConfigWrapper {
73+
cfg := nosqldb.Config{}
74+
return &ConfigWrapper{
75+
Config: &cfg,
76+
}
77+
}
78+
79+
func NRCreateClient(cfg *ConfigWrapper) (*ClientWrapper, error) {
80+
client, err := nosqldb.NewClient(*cfg.Config)
81+
if err != nil {
82+
return nil, fmt.Errorf("error creating OCI Client: %s", err.Error())
83+
}
84+
return &ClientWrapper{
85+
Client: client,
86+
Config: cfg.Config,
87+
}, nil
88+
}
89+
90+
// extractHostPort extracts host and port from an endpoint URL
91+
func extractHostPort(endpoint string) (host, port string) {
92+
if endpoint == "" {
93+
return "", ""
94+
}
95+
96+
// Parse the endpoint URL
97+
parsedURL, err := url.Parse(endpoint)
98+
if err != nil {
99+
return "", ""
100+
}
101+
102+
host = parsedURL.Hostname()
103+
port = parsedURL.Port()
104+
105+
// Set default ports if not specified
106+
if port == "" {
107+
switch parsedURL.Scheme {
108+
case "https":
109+
port = "443"
110+
case "http":
111+
port = "80"
112+
}
113+
}
114+
115+
return host, port
116+
}
117+
118+
func extractRequestFields(req any) (string, string, string) {
119+
var collection, statement, namespace string
120+
switch r := req.(type) {
121+
case *nosqldb.TableRequest:
122+
collection = r.TableName
123+
statement = r.Statement
124+
namespace = r.Namespace
125+
default:
126+
// keep strings empty
127+
}
128+
return collection, statement, namespace
129+
}
130+
131+
// executeWithDatastoreSegment is a generic helper function that executes a query with a given function from the
132+
// OCI Client. It takes a type parameter T as any because of the different response types that are used within the
133+
// OCI Client. This function will take the transaction from the context (if it exists) and create a Datastore Segment.
134+
// It will then call whatever client function has been passed in.
135+
func executeWithDatastoreSegment[T any, R any](
136+
cw *ClientWrapper,
137+
ctx context.Context,
138+
rw *ClientRequestWrapper[R],
139+
fn func() (T, error),
140+
) (*ClientResponseWrapper[T], error) {
141+
142+
txn := newrelic.FromContext(ctx)
143+
if txn == nil {
144+
return nil, fmt.Errorf("error executing DoTableRequest, no transaction")
145+
}
146+
147+
// Extract host and port from config endpoint
148+
host, port := extractHostPort(cw.Config.Endpoint)
149+
collection, statement, namespace := extractRequestFields(rw.ClientRequest)
150+
sgmt := newrelic.DatastoreSegment{
151+
StartTime: txn.StartSegmentNow(),
152+
Product: newrelic.DatastoreOracle,
153+
Collection: collection,
154+
DatabaseName: namespace, // using the namespace as the database name in this instance
155+
ParameterizedQuery: statement,
156+
Host: host,
157+
PortPathOrID: port,
158+
}
159+
160+
responseWrapper := ClientResponseWrapper[T]{}
161+
res, err := fn() // call the client function
162+
responseWrapper.ClientResponse = res
163+
if err != nil {
164+
return &responseWrapper, fmt.Errorf("error making request: %s", err.Error())
165+
}
166+
167+
sgmt.End()
168+
169+
return &responseWrapper, nil
170+
}
171+
172+
// Wrapper for nosqldb.Client.DoTableRequest. Provide the ClientWrapper and Context as parameters in addition to the nosqldb.TableRequest.
173+
// Returns a ClientResponseWrapper[*nosqldb.TableResult] and error.
174+
func NRDoTableRequest(cw *ClientWrapper, ctx context.Context, req *nosqldb.TableRequest) (*ClientResponseWrapper[*nosqldb.TableResult], error) {
175+
return executeWithDatastoreSegment(cw, ctx, &ClientRequestWrapper[*nosqldb.TableRequest]{ClientRequest: req}, func() (*nosqldb.TableResult, error) {
176+
return cw.Client.DoTableRequest(req)
177+
})
178+
}
179+
180+
// Wrapper for nosqldb.Client.DoTableRequestWait. Provide the ClientWrapper and Context as parameters in addition to the nosqldb.TableRequest,
181+
// timeout, and pollInterval. Returns a ClientResponseWrapper[*nosqldb.TableResult] and error.
182+
func NRDoTableRequestAndWait(cw *ClientWrapper, ctx context.Context, req *nosqldb.TableRequest, timeout time.Duration, pollInterval time.Duration) (*ClientResponseWrapper[*nosqldb.TableResult], error) {
183+
return executeWithDatastoreSegment(cw, ctx, &ClientRequestWrapper[*nosqldb.TableRequest]{ClientRequest: req}, func() (*nosqldb.TableResult, error) {
184+
return cw.Client.DoTableRequestAndWait(req, timeout, pollInterval)
185+
})
186+
}

0 commit comments

Comments
 (0)