@@ -113,43 +113,42 @@ func NewUsageService(db *db.DB, cfg *cfg.Cfg, logger *logger.Logger) *UsageServi
113113
114114// TrackUsage increments cost for a user/project in hourly, weekly, and lifetime buckets.
115115// Uses upsert to create or update the usage records atomically.
116- func (s * UsageService ) TrackUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 ) error {
116+ // The success parameter indicates whether the request completed successfully.
117+ // We will be charging only for successful requests, but we track failed requests for monitoring.
118+ func (s * UsageService ) TrackUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 , success bool ) error {
117119 if cost == 0 {
118120 return nil
119121 }
120122
121123 now := time .Now ()
122124
123125 // Track hourly usage
124- if err := s .trackHourlyUsage (ctx , userID , projectID , cost , now ); err != nil {
126+ if err := s .trackHourlyUsage (ctx , userID , projectID , cost , success , now ); err != nil {
125127 return err
126128 }
127129
128130 // Track weekly usage
129- if err := s .trackWeeklyUsage (ctx , userID , projectID , cost , now ); err != nil {
131+ if err := s .trackWeeklyUsage (ctx , userID , projectID , cost , success , now ); err != nil {
130132 return err
131133 }
132134
133135 // Track lifetime usage
134- if err := s .trackLifetimeUsage (ctx , userID , projectID , cost , now ); err != nil {
136+ if err := s .trackLifetimeUsage (ctx , userID , projectID , cost , success , now ); err != nil {
135137 return err
136138 }
137139
138140 return nil
139141}
140142
141- func (s * UsageService ) trackHourlyUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 , now time.Time ) error {
142- hourBucket := models .TruncateToHour (now )
143-
144- filter := bson.M {
145- "user_id" : userID ,
146- "project_id" : projectID ,
147- "hour_bucket" : bson .NewDateTimeFromTime (hourBucket ),
143+ func (s * UsageService ) upsertUsage (ctx context.Context , collection * mongo.Collection , filter bson.M , cost float64 , success bool , now time.Time ) error {
144+ costField := "failed_cost"
145+ if success {
146+ costField = "success_cost"
148147 }
149148
150149 update := bson.M {
151150 "$inc" : bson.M {
152- "cost" : cost ,
151+ costField : cost ,
153152 },
154153 "$set" : bson.M {
155154 "updated_at" : bson .NewDateTimeFromTime (now ),
@@ -160,55 +159,32 @@ func (s *UsageService) trackHourlyUsage(ctx context.Context, userID bson.ObjectI
160159 }
161160
162161 opts := options .UpdateOne ().SetUpsert (true )
163- _ , err := s . hourlyCollection .UpdateOne (ctx , filter , update , opts )
162+ _ , err := collection .UpdateOne (ctx , filter , update , opts )
164163 return err
165164}
166165
167- func (s * UsageService ) trackWeeklyUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 , now time.Time ) error {
168- weekBucket := models .TruncateToWeek (now )
169-
166+ func (s * UsageService ) trackHourlyUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 , success bool , now time.Time ) error {
170167 filter := bson.M {
171168 "user_id" : userID ,
172169 "project_id" : projectID ,
173- "week_bucket " : bson .NewDateTimeFromTime (weekBucket ),
170+ "hour_bucket " : bson .NewDateTimeFromTime (models . TruncateToHour ( now ) ),
174171 }
172+ return s .upsertUsage (ctx , s .hourlyCollection , filter , cost , success , now )
173+ }
175174
176- update := bson.M {
177- "$inc" : bson.M {
178- "cost" : cost ,
179- },
180- "$set" : bson.M {
181- "updated_at" : bson .NewDateTimeFromTime (now ),
182- },
183- "$setOnInsert" : bson.M {
184- "_id" : bson .NewObjectID (),
185- },
175+ func (s * UsageService ) trackWeeklyUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 , success bool , now time.Time ) error {
176+ filter := bson.M {
177+ "user_id" : userID ,
178+ "project_id" : projectID ,
179+ "week_bucket" : bson .NewDateTimeFromTime (models .TruncateToWeek (now )),
186180 }
187-
188- opts := options .UpdateOne ().SetUpsert (true )
189- _ , err := s .weeklyCollection .UpdateOne (ctx , filter , update , opts )
190- return err
181+ return s .upsertUsage (ctx , s .weeklyCollection , filter , cost , success , now )
191182}
192183
193- func (s * UsageService ) trackLifetimeUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 , now time.Time ) error {
184+ func (s * UsageService ) trackLifetimeUsage (ctx context.Context , userID bson.ObjectID , projectID string , cost float64 , success bool , now time.Time ) error {
194185 filter := bson.M {
195186 "user_id" : userID ,
196187 "project_id" : projectID ,
197188 }
198-
199- update := bson.M {
200- "$inc" : bson.M {
201- "cost" : cost ,
202- },
203- "$set" : bson.M {
204- "updated_at" : bson .NewDateTimeFromTime (now ),
205- },
206- "$setOnInsert" : bson.M {
207- "_id" : bson .NewObjectID (),
208- },
209- }
210-
211- opts := options .UpdateOne ().SetUpsert (true )
212- _ , err := s .lifetimeCollection .UpdateOne (ctx , filter , update , opts )
213- return err
189+ return s .upsertUsage (ctx , s .lifetimeCollection , filter , cost , success , now )
214190}
0 commit comments