server_timing_sql.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package repository
  2. import (
  3. "context"
  4. "database/sql/driver"
  5. "errors"
  6. "io"
  7. "reflect"
  8. "time"
  9. "github.com/Wei-Shaw/sub2api/internal/pkg/servertiming"
  10. )
  11. type serverTimingConnector struct {
  12. base driver.Connector
  13. }
  14. func newServerTimingConnector(base driver.Connector) driver.Connector {
  15. return &serverTimingConnector{base: base}
  16. }
  17. func (c *serverTimingConnector) Connect(ctx context.Context) (driver.Conn, error) {
  18. startedAt := time.Now()
  19. conn, err := c.base.Connect(ctx)
  20. servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
  21. if err != nil {
  22. return nil, err
  23. }
  24. return &serverTimingConn{Conn: conn}, nil
  25. }
  26. func (c *serverTimingConnector) Driver() driver.Driver {
  27. return c.base.Driver()
  28. }
  29. type serverTimingConn struct {
  30. driver.Conn
  31. }
  32. func (c *serverTimingConn) Prepare(query string) (driver.Stmt, error) {
  33. stmt, err := c.Conn.Prepare(query)
  34. if err != nil {
  35. return nil, err
  36. }
  37. return &serverTimingStmt{Stmt: stmt}, nil
  38. }
  39. func (c *serverTimingConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
  40. startedAt := time.Now()
  41. var (
  42. stmt driver.Stmt
  43. err error
  44. )
  45. if preparer, ok := c.Conn.(driver.ConnPrepareContext); ok {
  46. stmt, err = preparer.PrepareContext(ctx, query)
  47. } else {
  48. stmt, err = c.Conn.Prepare(query)
  49. }
  50. servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
  51. if err != nil {
  52. return nil, err
  53. }
  54. return &serverTimingStmt{Stmt: stmt}, nil
  55. }
  56. func (c *serverTimingConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
  57. execer, ok := c.Conn.(driver.ExecerContext)
  58. if !ok {
  59. return nil, driver.ErrSkip
  60. }
  61. startedAt := time.Now()
  62. result, err := execer.ExecContext(ctx, query, args)
  63. servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
  64. return result, err
  65. }
  66. func (c *serverTimingConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
  67. queryer, ok := c.Conn.(driver.QueryerContext)
  68. if !ok {
  69. return nil, driver.ErrSkip
  70. }
  71. startedAt := time.Now()
  72. rows, err := queryer.QueryContext(ctx, query, args)
  73. servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
  74. if err != nil || rows == nil {
  75. return rows, err
  76. }
  77. return newServerTimingRows(ctx, rows), nil
  78. }
  79. func (c *serverTimingConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
  80. startedAt := time.Now()
  81. var (
  82. tx driver.Tx
  83. err error
  84. )
  85. if beginner, ok := c.Conn.(driver.ConnBeginTx); ok {
  86. tx, err = beginner.BeginTx(ctx, opts)
  87. } else {
  88. if opts.Isolation != driver.IsolationLevel(0) {
  89. return nil, errors.New("driver does not support non-default isolation")
  90. }
  91. if opts.ReadOnly {
  92. return nil, errors.New("driver does not support read-only transactions")
  93. }
  94. // The wrapper exposes ConnBeginTx, so it must retain database/sql's
  95. // legacy fallback for drivers that only implement Conn.Begin.
  96. tx, err = c.Conn.Begin() //nolint:staticcheck // Required driver compatibility fallback.
  97. }
  98. servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
  99. if err != nil || tx == nil {
  100. return tx, err
  101. }
  102. return &serverTimingTx{Tx: tx, ctx: ctx}, nil
  103. }
  104. func (c *serverTimingConn) Ping(ctx context.Context) error {
  105. if pinger, ok := c.Conn.(driver.Pinger); ok {
  106. startedAt := time.Now()
  107. err := pinger.Ping(ctx)
  108. servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
  109. return err
  110. }
  111. return nil
  112. }
  113. func (c *serverTimingConn) ResetSession(ctx context.Context) error {
  114. if resetter, ok := c.Conn.(driver.SessionResetter); ok {
  115. startedAt := time.Now()
  116. err := resetter.ResetSession(ctx)
  117. servertiming.RecordInterval(ctx, servertiming.MetricDatabase, startedAt, time.Now())
  118. return err
  119. }
  120. return nil
  121. }
  122. func (c *serverTimingConn) IsValid() bool {
  123. if validator, ok := c.Conn.(driver.Validator); ok {
  124. return validator.IsValid()
  125. }
  126. return true
  127. }
  128. func (c *serverTimingConn) CheckNamedValue(value *driver.NamedValue) error {
  129. if checker, ok := c.Conn.(driver.NamedValueChecker); ok {
  130. return checker.CheckNamedValue(value)
  131. }
  132. return driver.ErrSkip
  133. }
  134. type serverTimingStmt struct {
  135. driver.Stmt
  136. }
  137. func (s *serverTimingStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
  138. startedAt := time.Now()
  139. var (
  140. result driver.Result
  141. err error
  142. )
  143. if execer, ok := s.Stmt.(driver.StmtExecContext); ok {
  144. result, err = execer.ExecContext(ctx, args)
  145. } else {
  146. var values []driver.Value
  147. values, err = namedValues(args)
  148. if err == nil {
  149. // The wrapper exposes StmtExecContext and must preserve the fallback
  150. // database/sql would use for a legacy driver statement.
  151. result, err = s.Stmt.Exec(values) //nolint:staticcheck // Required driver compatibility fallback.
  152. }
  153. }
  154. servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
  155. return result, err
  156. }
  157. func (s *serverTimingStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
  158. startedAt := time.Now()
  159. var (
  160. rows driver.Rows
  161. err error
  162. )
  163. if queryer, ok := s.Stmt.(driver.StmtQueryContext); ok {
  164. rows, err = queryer.QueryContext(ctx, args)
  165. } else {
  166. var values []driver.Value
  167. values, err = namedValues(args)
  168. if err == nil {
  169. // The wrapper exposes StmtQueryContext and must preserve the fallback
  170. // database/sql would use for a legacy driver statement.
  171. rows, err = s.Stmt.Query(values) //nolint:staticcheck // Required driver compatibility fallback.
  172. }
  173. }
  174. servertiming.Record(ctx, servertiming.MetricDatabase, startedAt, time.Now(), 1)
  175. if err != nil || rows == nil {
  176. return rows, err
  177. }
  178. return newServerTimingRows(ctx, rows), nil
  179. }
  180. func (s *serverTimingStmt) CheckNamedValue(value *driver.NamedValue) error {
  181. if checker, ok := s.Stmt.(driver.NamedValueChecker); ok {
  182. return checker.CheckNamedValue(value)
  183. }
  184. return driver.ErrSkip
  185. }
  186. func namedValues(args []driver.NamedValue) ([]driver.Value, error) {
  187. values := make([]driver.Value, len(args))
  188. for i, arg := range args {
  189. if arg.Name != "" {
  190. return nil, errors.New("named parameters are not supported")
  191. }
  192. values[i] = arg.Value
  193. }
  194. return values, nil
  195. }
  196. type serverTimingRows struct {
  197. driver.Rows
  198. ctx context.Context
  199. }
  200. func newServerTimingRows(ctx context.Context, rows driver.Rows) *serverTimingRows {
  201. return &serverTimingRows{Rows: rows, ctx: ctx}
  202. }
  203. func (r *serverTimingRows) Close() error {
  204. startedAt := time.Now()
  205. err := r.Rows.Close()
  206. servertiming.RecordInterval(r.ctx, servertiming.MetricDatabase, startedAt, time.Now())
  207. return err
  208. }
  209. func (r *serverTimingRows) Next(dest []driver.Value) error {
  210. startedAt := time.Now()
  211. err := r.Rows.Next(dest)
  212. servertiming.RecordInterval(r.ctx, servertiming.MetricDatabase, startedAt, time.Now())
  213. return err
  214. }
  215. func (r *serverTimingRows) HasNextResultSet() bool {
  216. if rows, ok := r.Rows.(driver.RowsNextResultSet); ok {
  217. return rows.HasNextResultSet()
  218. }
  219. return false
  220. }
  221. func (r *serverTimingRows) NextResultSet() error {
  222. rows, ok := r.Rows.(driver.RowsNextResultSet)
  223. if !ok {
  224. return io.EOF
  225. }
  226. startedAt := time.Now()
  227. err := rows.NextResultSet()
  228. servertiming.RecordInterval(r.ctx, servertiming.MetricDatabase, startedAt, time.Now())
  229. return err
  230. }
  231. func (r *serverTimingRows) ColumnTypeScanType(index int) reflect.Type {
  232. if rows, ok := r.Rows.(driver.RowsColumnTypeScanType); ok {
  233. return rows.ColumnTypeScanType(index)
  234. }
  235. return reflect.TypeOf(new(any)).Elem()
  236. }
  237. func (r *serverTimingRows) ColumnTypeDatabaseTypeName(index int) string {
  238. if rows, ok := r.Rows.(driver.RowsColumnTypeDatabaseTypeName); ok {
  239. return rows.ColumnTypeDatabaseTypeName(index)
  240. }
  241. return ""
  242. }
  243. func (r *serverTimingRows) ColumnTypeLength(index int) (int64, bool) {
  244. if rows, ok := r.Rows.(driver.RowsColumnTypeLength); ok {
  245. return rows.ColumnTypeLength(index)
  246. }
  247. return 0, false
  248. }
  249. func (r *serverTimingRows) ColumnTypeNullable(index int) (bool, bool) {
  250. if rows, ok := r.Rows.(driver.RowsColumnTypeNullable); ok {
  251. return rows.ColumnTypeNullable(index)
  252. }
  253. return false, false
  254. }
  255. func (r *serverTimingRows) ColumnTypePrecisionScale(index int) (int64, int64, bool) {
  256. if rows, ok := r.Rows.(driver.RowsColumnTypePrecisionScale); ok {
  257. return rows.ColumnTypePrecisionScale(index)
  258. }
  259. return 0, 0, false
  260. }
  261. type serverTimingTx struct {
  262. driver.Tx
  263. ctx context.Context
  264. }
  265. func (t *serverTimingTx) Commit() error {
  266. startedAt := time.Now()
  267. err := t.Tx.Commit()
  268. servertiming.RecordInterval(t.ctx, servertiming.MetricDatabase, startedAt, time.Now())
  269. return err
  270. }
  271. func (t *serverTimingTx) Rollback() error {
  272. startedAt := time.Now()
  273. err := t.Tx.Rollback()
  274. servertiming.RecordInterval(t.ctx, servertiming.MetricDatabase, startedAt, time.Now())
  275. return err
  276. }