Skip to content

Commit 9fcc17e

Browse files
[release-23.0] BuiltinBackupEngine: Retry file close and fail backup when we cannot (#18848) (#18862)
Signed-off-by: Matt Lord <[email protected]> Co-authored-by: vitess-bot[bot] <108069721+vitess-bot[bot]@users.noreply.github.com>
1 parent 48ac9a1 commit 9fcc17e

File tree

5 files changed

+1072
-24
lines changed

5 files changed

+1072
-24
lines changed

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CLAUDE.md

CLAUDE.md

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
## :handshake: Our Partnership
2+
3+
**We're building this together.** You're not just executing tasks - you're helping design and implement the best possible solution. This means:
4+
5+
- Challenge my suggestions when something feels wrong
6+
- Ask me to explain my reasoning
7+
- Propose alternative approaches
8+
- Take time to think through problems
9+
10+
**Quality is non-negotiable.** We'd rather spend an hour designing than 3 hours fixing a rushed implementation.
11+
12+
## :thought_balloon: Before We Code
13+
14+
Always discuss first:
15+
- What problem are we solving?
16+
- What's the ideal solution?
17+
- What tests would prove it works?
18+
- Are we making the codebase better?
19+
20+
## Strict Task Adherence
21+
22+
**Only do exactly what I ask for - nothing more, nothing less.**
23+
24+
- Do NOT proactively update documentation unless explicitly requested
25+
- Do NOT add explanatory comments unless asked
26+
- Do NOT make "improvements" or "clean up" code beyond the specific task
27+
- Do NOT add features, optimizations, or enhancements I didn't mention
28+
- If there is something you think should be done, suggest it, but don't do it until asked to
29+
30+
**Red flags that indicate you're going beyond the task:**
31+
- "Let me also..."
32+
- "While I'm at it..."
33+
- "I should also update..."
34+
- "Let me improve..."
35+
- "I'll also clean up..."
36+
37+
**If the task is complete, STOP. Don't look for more work to do.**
38+
39+
## :test_tube: Test-Driven Development
40+
41+
TDD isn't optional - it's how we ensure quality:
42+
43+
### The TDD Cycle
44+
1. **Red** - Write a failing test that defines success
45+
2. **Green** - Write minimal code to pass
46+
3. **Refactor** - Make it clean and elegant
47+
48+
### Example TDD Session
49+
```go
50+
// Step 1: Write the test first
51+
func TestConnectionBilateralCleanup(t *testing.T) {
52+
// Define what success looks like
53+
client, server := testutils.CreateConnectedTCPPair()
54+
55+
// Test the behavior we want
56+
client.Close()
57+
58+
// Both sides should be closed
59+
assert.Eventually(t, func() bool {
60+
return isConnectionClosed(server)
61+
})
62+
}
63+
64+
// Step 2: See it fail (confirms we're testing the right thing)
65+
// Step 3: Implement the feature
66+
// Step 4: See it pass
67+
// Step 5: Refactor for clarity
68+
```
69+
70+
To make sure tests are easy to read, we use testify assertions. Make sure to use assert.Eventually instead of using manual thread.sleep and timeouts.
71+
72+
## :rotating_light: Error Handling Excellence
73+
74+
Error handling is not an afterthought - it's core to reliable software.
75+
76+
### Go Error Patterns
77+
```go
78+
// YES - Clear error context
79+
func ProcessUser(id string) (*User, error) {
80+
if id == "" {
81+
return nil, fmt.Errorf("user ID cannot be empty")
82+
}
83+
84+
user, err := db.GetUser(id)
85+
if err != nil {
86+
return nil, fmt.Errorf("failed to get user %s: %w", id, err)
87+
}
88+
89+
return user, nil
90+
}
91+
92+
// NO - Swallowing errors
93+
func ProcessUser(id string) *User {
94+
user, _ := db.GetUser(id) // What if this fails?
95+
return user
96+
}
97+
```
98+
99+
### Error Handling Principles
100+
1. **Wrap errors with context** - Use `fmt.Errorf("context: %w", err)`
101+
2. **Validate early** - Check inputs before doing work
102+
3. **Fail fast** - Don't continue with invalid state
103+
4. **Log appropriately** - Errors at boundaries, debug info internally
104+
5. **Return structured errors** - Use error types for different handling
105+
106+
### Testing Error Paths
107+
```go
108+
func TestProcessUser_InvalidID(t *testing.T) {
109+
_, err := ProcessUser("")
110+
assert.ErrorContains(t, err, "cannot be empty")
111+
}
112+
113+
func TestProcessUser_DatabaseError(t *testing.T) {
114+
mockDB.EXPECT().GetUser("123").Return(nil, errors.New("db connection failed"))
115+
116+
_, err := ProcessUser("123")
117+
assert.ErrorContains(t, err, "failed to get user")
118+
}
119+
```
120+
121+
## :triangular_ruler: Design Principles
122+
123+
### 1. Simple is Better Than Clever
124+
```go
125+
// YES - Clear and obvious
126+
if user.NeedsMigration() {
127+
return migrate(user)
128+
}
129+
130+
// NO - Clever but unclear
131+
return user.NeedsMigration() && migrate(user) || user
132+
```
133+
134+
### 2. Explicit is Better Than Implicit
135+
- Clear function names
136+
- Obvious parameter types
137+
- No hidden side effects
138+
139+
### 3. Performance with Clarity
140+
- Optimize hot paths
141+
- But keep code readable
142+
- Document why, not what
143+
144+
### 4. Fail Fast and Clearly
145+
- Validate inputs early
146+
- Return clear error messages
147+
- Help future debugging
148+
149+
### 5. Interfaces Define What You Need, Not What You Provide
150+
- When you need something from another component, define the interface in your package
151+
- Don't look at what someone else provides - define exactly what you require
152+
- This keeps interfaces small, focused, and prevents unnecessary coupling
153+
- Types and their methods live together. At the top of files, use a single ```type ()``` with all type declarations inside.
154+
155+
### 6. Go-Specific Best Practices
156+
- **Receiver naming** - Use consistent, short receiver names (e.g., `u *User`, not `user *User`)
157+
- **Package naming** - Short, descriptive, lowercase without underscores
158+
- **Interface naming** - Single-method interfaces end in `-er` (Reader, Writer, Handler)
159+
- **Context first** - Always pass `context.Context` as the first parameter
160+
- **Channels for coordination** - Use channels to coordinate goroutines, not shared memory
161+
162+
## :mag: Dubugging & Troubleshooting
163+
164+
When things don't work as expected, we debug systematically:
165+
166+
### Debugging Strategy
167+
1. **Reproduce reliably** - Create a minimal failing case
168+
2. **Isolate the problem** - Binary search through the system
169+
3. **Understand the data flow** - Trace inputs and outputs
170+
4. **Question assumptions** - What did we assume was working?
171+
5. **Fix the root cause** - Not just the symptoms
172+
173+
### Debugging Tools & Techniques
174+
```go
175+
// Use structured logging for debugging
176+
log.WithFields(log.Fields{
177+
"user_id": userID,
178+
"action": "process_payment",
179+
"amount": amount,
180+
}).Debug("Starting payment processing")
181+
182+
// Add strategic debug points
183+
func processPayment(amount float64) error {
184+
log.Debugf("processPayment called with amount: %f", amount)
185+
186+
if amount <= 0 {
187+
return fmt.Errorf("invalid amount: %f", amount)
188+
}
189+
190+
// More processing...
191+
log.Debug("Payment validation passed")
192+
return nil
193+
}
194+
```
195+
196+
### When Stuck
197+
- Write a test that reproduces the issue
198+
- Add logging to understand data flow
199+
- Use the debugger to step through code
200+
- Rubber duck explain the problem
201+
- Take a break and come back fresh
202+
203+
## :recycle: Refactoring Legacy Code
204+
205+
When improving existing code, we move carefully and systematically:
206+
207+
### Refactoring Strategy
208+
1. **Understand first** - Read and comprehend the existing code
209+
2. **Add tests** - Create safety nets before changing anything
210+
3. **Small steps** - Make tiny, verifiable improvements
211+
4. **Preserve behavior** - Keep the same external interface
212+
5. **Measure improvement** - Verify it's actually better
213+
214+
### Safe Refactoring Process
215+
```go
216+
// Step 1: Add characterization tests
217+
func TestLegacyProcessor_ExistingBehavior(t *testing.T) {
218+
processor := &LegacyProcessor{}
219+
220+
// Document current behavior, even if it seems wrong
221+
result := processor.Process("input")
222+
assert.Equal(t, "weird_legacy_output", result)
223+
}
224+
225+
// Step 2: Refactor with tests passing
226+
func (p *LegacyProcessor) Process(input string) string {
227+
// Improved implementation that maintains the same behavior
228+
return processWithNewLogic(input)
229+
}
230+
231+
// Step 3: Now we can safely change the behavior
232+
func TestProcessor_ImprovedBehavior(t *testing.T) {
233+
processor := &Processor{}
234+
235+
result := processor.Process("input")
236+
assert.Equal(t, "expected_output", result)
237+
}
238+
```
239+
240+
## :arrows_counterclockwise: Development Workflow
241+
242+
### Starting a Feature
243+
1. **Discuss** - "I'm thinking about implementing X. Here's my approach..."
244+
2. **Design** - Sketch out the API and key components
245+
3. **Test** - Write tests that define the behavior
246+
4. **Implement** - Make the tests pass
247+
5. **Review** - "Does this make sense? Any concerns?"
248+
249+
### Making Changes
250+
1. **Small PRs** - Easier to review and less risky
251+
2. **Incremental** - Build features piece by piece
252+
3. **Always tested** - No exceptions
253+
4. **Clear commits** - Each commit should have a clear purpose
254+
255+
### Git and PR Workflow
256+
257+
**CRITICAL: Git commands are ONLY for reading state - NEVER for modifying it.**
258+
- **NEVER** use git commands that modify the filesystem unless explicitly told to commit
259+
- You may read git state: `git status`, `git log`, `git diff`, `git branch --show-current`
260+
- You may NOT: `git commit`, `git add`, `git reset`, `git checkout`, `git restore`, `git rebase`, `git push`, etc.
261+
- **ONLY commit when explicitly asked to commit**
262+
- When asked to commit, do it once and stop
263+
- Only I can modify git state unless you've been given explicit permission to commit
264+
265+
**Once a PR is created, NEVER amend commits or rewrite history.**
266+
- Always create new commits after PR is created
267+
- No `git commit --amend` after pushing to a PR branch
268+
- No `git rebase` that rewrites commits in the PR
269+
- No force pushes to PR branches
270+
- This keeps the PR history clean and reviewable
271+
272+
**When asked to write a PR description:**
273+
1. **Use `gh` CLI** - Always use `gh pr edit <number>` to update PRs
274+
2. **Update both body and title** - Use `--body` and `--title` flags
275+
3. **Be informal, humble, and short** - Keep it conversational and to the point
276+
4. **Credit appropriately** - If Claude Code wrote most of it, mention that
277+
5. **Example format**:
278+
```
279+
## What's this?
280+
[Brief explanation of the feature/fix]
281+
282+
## How it works
283+
[Key implementation details]
284+
285+
## Usage
286+
[Code examples if relevant]
287+
288+
---
289+
_Most of this was written by Claude Code - I just provided direction._
290+
```
291+
292+
## :memo: Code Review Mindset
293+
294+
When reviewing code (yours or mine), ask:
295+
- Is this the simplest solution?
296+
- Will this make sense in 6 months?
297+
- Are edge cases handled?
298+
- Is it well tested?
299+
- Does it improve the codebase?
300+
301+
## :dart: Common Patterns
302+
303+
### Feature Implementation
304+
```
305+
You: "Let's add feature X"
306+
Me: "Sounds good! What's the API going to look like? What are the main use cases?"
307+
[Discussion of design]
308+
Me: "Let me write some tests to clarify the behavior we want"
309+
[TDD implementation]
310+
Me: "Here's what I've got. What do you think?"
311+
```
312+
313+
### Bug Fixing
314+
```
315+
You: "We have a bug where X happens"
316+
Me: "Let's write a test that reproduces it first"
317+
[Test that fails]
318+
Me: "Great, now we know exactly what we're fixing"
319+
[Fix implementation]
320+
```
321+
322+
### Performance Work
323+
```
324+
You: "This seems slow"
325+
Me: "Let's benchmark it first to get a baseline"
326+
[Benchmark results]
327+
Me: "Now let's optimize without breaking functionality"
328+
[Optimization with tests passing]
329+
```
330+
331+
## :rocket: Shipping Quality
332+
333+
Before considering any work "done":
334+
- [ ] Tests pass and cover the feature
335+
- [ ] Code is clean and readable
336+
- [ ] Edge cases are handled
337+
- [ ] Performance is acceptable
338+
- [ ] Documentation is updated if needed
339+
- [ ] We're both happy with it
340+
341+
Remember: We're crafting software, not just making it work. Every line of code is an opportunity to make the system better.

0 commit comments

Comments
 (0)