|
1 | 1 | package main
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "context" |
5 |
| - "errors" |
6 |
| - "fmt" |
7 |
| - "time" |
| 4 | + "database/sql" |
| 5 | + "log" |
8 | 6 | )
|
9 | 7 |
|
10 | 8 | // =============================================================================
|
11 | 9 |
|
12 |
| -// This code defined a concrete type named user with a name and email field. |
13 |
| - |
14 |
| -type user struct { |
15 |
| - name string |
16 |
| - email string |
| 10 | +type User struct { |
| 11 | + ID int64 |
| 12 | + Name string |
| 13 | + Email string |
17 | 14 | }
|
18 | 15 |
|
19 |
| -// ============================================================================= |
20 |
| - |
21 |
| -// WorkerInterface defines a function signature that takes a context value as |
22 |
| -// input and returns any value (via the empty interface) and an error. |
23 |
| -// |
24 |
| -// The retryInterface function provides functionality to execute a worker |
25 |
| -// function and if it fails, calls it again until the context times out. The |
26 |
| -// function returns back to the caller the values returned from the worker |
27 |
| -// function. |
28 |
| - |
29 |
| -type WorkerInterface func(ctx context.Context) (interface{}, error) |
| 16 | +func InsertUser(db *sql.DB, u User) (User, error) { |
| 17 | + const query = "insert into users (name, email) values ($1, $2)" |
| 18 | + result, err := ExecuteQuery(query, u.Name, u.Email) |
| 19 | + if err != nil { |
| 20 | + return User{}, err |
| 21 | + } |
30 | 22 |
|
31 |
| -func retryInterface(ctx context.Context, interval time.Duration, worker WorkerInterface) (interface{}, error) { |
32 |
| - if ctx.Err() != nil { |
33 |
| - return nil, errors.New("error") |
| 23 | + id, err := result.LastInsertId() |
| 24 | + if err != nil { |
| 25 | + return User{}, err |
34 | 26 | }
|
35 | 27 |
|
36 |
| - var retry *time.Timer |
| 28 | + u.ID = id |
| 29 | + return u, nil |
| 30 | +} |
37 | 31 |
|
38 |
| - for { |
39 |
| - if value, err := worker(ctx); err == nil { |
40 |
| - return value, nil |
41 |
| - } |
| 32 | +type Customer struct { |
| 33 | + ID int64 |
| 34 | + Name string |
| 35 | + Email string |
| 36 | +} |
42 | 37 |
|
43 |
| - if retry == nil { |
44 |
| - retry = time.NewTimer(interval) |
45 |
| - } |
| 38 | +func InsertCustomer(db *sql.DB, c Customer) (Customer, error) { |
| 39 | + const query = "insert into customers (name, email) values ($1, $2)" |
| 40 | + result, err := ExecuteQuery(query, c.Name, c.Email) |
| 41 | + if err != nil { |
| 42 | + return Customer{}, err |
| 43 | + } |
46 | 44 |
|
47 |
| - select { |
48 |
| - case <-ctx.Done(): |
49 |
| - retry.Stop() |
50 |
| - return nil, errors.New("error") |
51 |
| - case <-retry.C: |
52 |
| - retry.Reset(interval) |
53 |
| - } |
| 45 | + id, err := result.LastInsertId() |
| 46 | + if err != nil { |
| 47 | + return Customer{}, err |
54 | 48 | }
|
| 49 | + |
| 50 | + c.ID = id |
| 51 | + return c, nil |
55 | 52 | }
|
56 | 53 |
|
57 | 54 | // =============================================================================
|
58 | 55 |
|
59 |
| -// This is a generics version of the last example. |
60 |
| -// |
61 |
| -// WorkerGeneric defines a function signature that takes a context value as |
62 |
| -// input and returns any value of type Result (to be determined at compile time) |
63 |
| -// and an error. |
64 |
| -// |
65 |
| -// The retryGeneric function provides functionality to execute a worker |
66 |
| -// function and if it fails, calls it again until the context times out. The |
67 |
| -// function returns back to the caller the values returned from the worker |
68 |
| -// function. In this case, the returned values are based on type Result and not |
69 |
| -// the empty interface. |
70 |
| -// |
71 |
| -// Returning zero value is an interesting aspect of this code. A variable |
72 |
| -// named zero of type Result is constructed at the return site and used for the |
73 |
| -// return statement. |
74 |
| - |
75 |
| -type WorkerGeneric(type Result) func(ctx context.Context) (Result, error) |
76 |
| - |
77 |
| -func retryGeneric(type Result)(ctx context.Context, interval time.Duration, worker WorkerGeneric(Result)) (Result, error) { |
78 |
| - if ctx.Err() != nil { |
79 |
| - var zero Result |
80 |
| - return zero, errors.New("error") |
| 56 | +type inserter interface { |
| 57 | + type User, Customer |
| 58 | +} |
| 59 | + |
| 60 | +func Insert(type T inserter)(db *sql.DB, entity T, query string, args ...interface{}) (T, error) { |
| 61 | + var zero T |
| 62 | + |
| 63 | + result, err := ExecuteQuery(query, args...) |
| 64 | + if err != nil { |
| 65 | + return zero, err |
81 | 66 | }
|
82 | 67 |
|
83 |
| - var retry *time.Timer |
84 |
| - |
85 |
| - for { |
86 |
| - if value, err := worker(ctx); err == nil { |
87 |
| - return value, nil |
88 |
| - } |
89 |
| - |
90 |
| - if retry == nil { |
91 |
| - retry = time.NewTimer(interval) |
92 |
| - } |
93 |
| - |
94 |
| - select { |
95 |
| - case <-ctx.Done(): |
96 |
| - retry.Stop() |
97 |
| - var zero Result |
98 |
| - return zero, errors.New("error") |
99 |
| - case <-retry.C: |
100 |
| - retry.Reset(interval) |
101 |
| - } |
| 68 | + id, err := result.LastInsertId() |
| 69 | + if err != nil { |
| 70 | + return zero, err |
102 | 71 | }
|
| 72 | + |
| 73 | + entity.ID = id |
| 74 | + return entity, nil |
| 75 | +} |
| 76 | + |
| 77 | +// ============================================================================= |
| 78 | + |
| 79 | +type Result struct{} |
| 80 | + |
| 81 | +func (r Result) LastInsertId() (int64, error) { return 1, nil } |
| 82 | +func (r Result) RowsAffected() (int64, error) { return 1, nil } |
| 83 | + |
| 84 | +func ExecuteQuery(query string, args ...interface{}) (sql.Result, error) { |
| 85 | + return Result{}, nil |
103 | 86 | }
|
104 | 87 |
|
105 | 88 | // =============================================================================
|
106 | 89 |
|
107 | 90 | func main() {
|
108 |
| - ctx := context.Background() |
| 91 | + var db *sql.DB |
109 | 92 |
|
110 |
| - workerInt := func(ctx context.Context) (interface{}, error) { |
111 |
| - time.Sleep(time.Millisecond) |
112 |
| - return &user{"bill", " [email protected]"}, nil |
| 93 | + var u User |
| 94 | + query := "insert into users (name, email) values ($1, $2)" |
| 95 | + u, err := Insert(db, u, query, u.Name, u.Email) |
| 96 | + if err != nil { |
| 97 | + log.Fatal(err) |
113 | 98 | }
|
114 |
| - intf, err := retryInterface(ctx, time.Second, workerInt) |
115 |
| - fmt.Println(intf.(*user), err) |
| 99 | + log.Println(u) |
116 | 100 |
|
117 |
| - workerGen := func(ctx context.Context) (*user, error) { |
118 |
| - time.Sleep(time.Millisecond) |
119 |
| - return &user{"bill", " [email protected]"}, nil |
| 101 | + var c Customer |
| 102 | + query := "insert into customers (name, email) values ($1, $2)" |
| 103 | + c, err := Insert(db, c, query, u.Name, u.Email) |
| 104 | + if err != nil { |
| 105 | + log.Fatal(err) |
120 | 106 | }
|
121 |
| - user, err := retryGeneric(*user)(ctx, time.Second, workerGen) |
122 |
| - fmt.Println(user, err) |
| 107 | + log.Println(c) |
123 | 108 | }
|
0 commit comments