tangled
alpha
login
or
join now
atscan.net
/
plcbundle
A Transparent and Verifiable Way to Sync the AT Protocol's PLC Directory
14
fork
atom
overview
issues
2
pulls
pipelines
cmd mempool
tree.fail
3 months ago
99fb2e58
f66a7546
+259
-103
8 changed files
expand all
collapse all
unified
split
cmd
plcbundle
commands
did.go
diff.go
mempool.go
server.go
status.go
stream.go
sync.go
main.go
-1
cmd/plcbundle/commands/did.go
···
1
1
-
// repo/cmd/plcbundle/commands/did.go
2
1
package commands
3
2
4
3
import (
-1
cmd/plcbundle/commands/diff.go
···
1
1
-
// repo/cmd/plcbundle/commands/diff.go
2
1
package commands
3
2
4
3
import (
+257
-95
cmd/plcbundle/commands/mempool.go
···
7
7
"strings"
8
8
"time"
9
9
10
10
-
flag "github.com/spf13/pflag"
11
11
-
10
10
+
"github.com/goccy/go-json"
11
11
+
"github.com/spf13/cobra"
12
12
"tangled.org/atscan.net/plcbundle/internal/types"
13
13
)
14
14
15
15
-
// MempoolCommand handles the mempool subcommand
16
16
-
func MempoolCommand(args []string) error {
17
17
-
fs := flag.NewFlagSet("mempool", flag.ExitOnError)
18
18
-
clear := fs.Bool("clear", false, "clear the mempool")
19
19
-
export := fs.Bool("export", false, "export mempool operations as JSONL to stdout")
20
20
-
refresh := fs.Bool("refresh", false, "reload mempool from disk")
21
21
-
validate := fs.Bool("validate", false, "validate chronological order")
22
22
-
verbose := fs.Bool("v", false, "verbose output")
15
15
+
func NewMempoolCommand() *cobra.Command {
16
16
+
cmd := &cobra.Command{
17
17
+
Use: "mempool [status]",
18
18
+
Aliases: []string{"mp"},
19
19
+
Short: "Manage mempool operations",
20
20
+
Long: `Manage mempool operations
23
21
24
24
-
if err := fs.Parse(args); err != nil {
25
25
-
return err
22
22
+
The mempool stores operations waiting to be bundled. It maintains
23
23
+
strict chronological order and automatically validates consistency.`,
24
24
+
25
25
+
Example: ` # Show mempool status
26
26
+
plcbundle mempool
27
27
+
plcbundle mempool status
28
28
+
29
29
+
# Clear all operations
30
30
+
plcbundle mempool clear
31
31
+
32
32
+
# Export operations as JSONL
33
33
+
plcbundle mempool dump
34
34
+
plcbundle mempool dump > operations.jsonl
35
35
+
36
36
+
# Using alias
37
37
+
plcbundle mp status`,
38
38
+
39
39
+
RunE: func(cmd *cobra.Command, args []string) error {
40
40
+
// Default to status subcommand
41
41
+
return mempoolStatus(cmd, args)
42
42
+
},
26
43
}
27
44
28
28
-
mgr, dir, err := getManager("")
45
45
+
// Add subcommands
46
46
+
cmd.AddCommand(newMempoolStatusCommand())
47
47
+
cmd.AddCommand(newMempoolClearCommand())
48
48
+
cmd.AddCommand(newMempoolDumpCommand())
49
49
+
50
50
+
return cmd
51
51
+
}
52
52
+
53
53
+
// ============================================================================
54
54
+
// MEMPOOL STATUS - Show current state
55
55
+
// ============================================================================
56
56
+
57
57
+
func newMempoolStatusCommand() *cobra.Command {
58
58
+
var verbose bool
59
59
+
60
60
+
cmd := &cobra.Command{
61
61
+
Use: "status",
62
62
+
Aliases: []string{"s", "info"},
63
63
+
Short: "Show mempool status",
64
64
+
Long: `Show mempool status and statistics
65
65
+
66
66
+
Displays current mempool state including operation count, progress toward
67
67
+
next bundle, validation status, and memory usage.`,
68
68
+
69
69
+
Example: ` # Show status
70
70
+
plcbundle mempool status
71
71
+
plcbundle mempool
72
72
+
73
73
+
# Verbose output with samples
74
74
+
plcbundle mempool status -v`,
75
75
+
76
76
+
RunE: func(cmd *cobra.Command, args []string) error {
77
77
+
return mempoolStatus(cmd, args)
78
78
+
},
79
79
+
}
80
80
+
81
81
+
cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Show sample operations")
82
82
+
83
83
+
return cmd
84
84
+
}
85
85
+
86
86
+
func mempoolStatus(cmd *cobra.Command, args []string) error {
87
87
+
verbose, _ := cmd.Flags().GetBool("verbose")
88
88
+
if cmd.Parent() != nil {
89
89
+
// Called as subcommand, check parent's verbose flag
90
90
+
if v, err := cmd.Parent().Flags().GetBool("verbose"); err == nil && v {
91
91
+
verbose = true
92
92
+
}
93
93
+
}
94
94
+
95
95
+
mgr, dir, err := getManagerFromCommand(cmd, "")
29
96
if err != nil {
30
97
return err
31
98
}
32
99
defer mgr.Close()
33
100
34
34
-
fmt.Printf("Working in: %s\n\n", dir)
101
101
+
return showMempoolStatus(mgr, dir, verbose)
102
102
+
}
35
103
36
36
-
// Handle validate
37
37
-
if *validate {
38
38
-
fmt.Printf("Validating mempool chronological order...\n")
39
39
-
if err := mgr.ValidateMempool(); err != nil {
40
40
-
return fmt.Errorf("validation failed: %w", err)
41
41
-
}
42
42
-
fmt.Printf("✓ Mempool validation passed\n")
43
43
-
return nil
44
44
-
}
104
104
+
// ============================================================================
105
105
+
// MEMPOOL CLEAR - Remove all operations
106
106
+
// ============================================================================
45
107
46
46
-
// Handle refresh
47
47
-
if *refresh {
48
48
-
fmt.Printf("Refreshing mempool from disk...\n")
49
49
-
if err := mgr.RefreshMempool(); err != nil {
50
50
-
return fmt.Errorf("refresh failed: %w", err)
51
51
-
}
108
108
+
func newMempoolClearCommand() *cobra.Command {
109
109
+
var force bool
52
110
53
53
-
if err := mgr.ValidateMempool(); err != nil {
54
54
-
fmt.Fprintf(os.Stderr, "⚠️ Warning: mempool validation failed after refresh: %v\n", err)
55
55
-
} else {
56
56
-
fmt.Printf("✓ Mempool refreshed and validated\n\n")
57
57
-
}
58
58
-
}
111
111
+
cmd := &cobra.Command{
112
112
+
Use: "clear",
113
113
+
Aliases: []string{"c", "reset"},
114
114
+
Short: "Clear mempool operations",
115
115
+
Long: `Clear all operations from mempool
59
116
60
60
-
// Handle clear
61
61
-
if *clear {
62
62
-
stats := mgr.GetMempoolStats()
63
63
-
count := stats["count"].(int)
117
117
+
Removes all operations from the mempool and saves the empty state.
118
118
+
This is a destructive operation that cannot be undone.
64
119
65
65
-
if count == 0 {
66
66
-
fmt.Println("Mempool is already empty")
67
67
-
return nil
68
68
-
}
120
120
+
Use cases:
121
121
+
• Reset after testing
122
122
+
• Clear corrupted data
123
123
+
• Force fresh start`,
69
124
70
70
-
fmt.Printf("⚠️ This will clear %d operations from the mempool.\n", count)
71
71
-
fmt.Printf("Are you sure? [y/N]: ")
72
72
-
var response string
73
73
-
fmt.Scanln(&response)
74
74
-
if strings.ToLower(strings.TrimSpace(response)) != "y" {
75
75
-
fmt.Println("Cancelled")
76
76
-
return nil
77
77
-
}
125
125
+
Example: ` # Clear with confirmation
126
126
+
plcbundle mempool clear
78
127
79
79
-
if err := mgr.ClearMempool(); err != nil {
80
80
-
return fmt.Errorf("clear failed: %w", err)
81
81
-
}
128
128
+
# Force clear without confirmation
129
129
+
plcbundle mempool clear --force
130
130
+
plcbundle mempool clear -f`,
82
131
83
83
-
fmt.Printf("✓ Mempool cleared (%d operations removed)\n", count)
84
84
-
return nil
85
85
-
}
132
132
+
RunE: func(cmd *cobra.Command, args []string) error {
133
133
+
mgr, dir, err := getManagerFromCommand(cmd, "")
134
134
+
if err != nil {
135
135
+
return err
136
136
+
}
137
137
+
defer mgr.Close()
86
138
87
87
-
// Handle export
88
88
-
if *export {
89
89
-
ops, err := mgr.GetMempoolOperations()
90
90
-
if err != nil {
91
91
-
return fmt.Errorf("failed to get mempool operations: %w", err)
92
92
-
}
139
139
+
stats := mgr.GetMempoolStats()
140
140
+
count := stats["count"].(int)
93
141
94
94
-
if len(ops) == 0 {
95
95
-
fmt.Fprintf(os.Stderr, "Mempool is empty\n")
142
142
+
if count == 0 {
143
143
+
fmt.Println("Mempool is already empty")
144
144
+
return nil
145
145
+
}
146
146
+
147
147
+
fmt.Printf("Working in: %s\n\n", dir)
148
148
+
149
149
+
if !force {
150
150
+
fmt.Printf("⚠️ This will clear %d operations from the mempool.\n", count)
151
151
+
fmt.Printf("Are you sure? [y/N]: ")
152
152
+
var response string
153
153
+
fmt.Scanln(&response)
154
154
+
if strings.ToLower(strings.TrimSpace(response)) != "y" {
155
155
+
fmt.Println("Cancelled")
156
156
+
return nil
157
157
+
}
158
158
+
}
159
159
+
160
160
+
if err := mgr.ClearMempool(); err != nil {
161
161
+
return fmt.Errorf("clear failed: %w", err)
162
162
+
}
163
163
+
164
164
+
fmt.Printf("\n✓ Mempool cleared (%d operations removed)\n", count)
96
165
return nil
97
97
-
}
166
166
+
},
167
167
+
}
168
168
+
169
169
+
cmd.Flags().BoolVarP(&force, "force", "f", false, "Skip confirmation prompt")
98
170
99
99
-
for _, op := range ops {
100
100
-
if len(op.RawJSON) > 0 {
101
101
-
fmt.Println(string(op.RawJSON))
171
171
+
return cmd
172
172
+
}
173
173
+
174
174
+
// ============================================================================
175
175
+
// MEMPOOL DUMP - Export operations as JSONL
176
176
+
// ============================================================================
177
177
+
178
178
+
func newMempoolDumpCommand() *cobra.Command {
179
179
+
var outputFile string
180
180
+
181
181
+
cmd := &cobra.Command{
182
182
+
Use: "dump",
183
183
+
Aliases: []string{"export", "d"},
184
184
+
Short: "Export mempool as JSONL",
185
185
+
Long: `Export mempool operations as JSONL
186
186
+
187
187
+
Outputs all operations in the mempool as newline-delimited JSON.
188
188
+
Perfect for backup, analysis, or piping to other tools.`,
189
189
+
190
190
+
Example: ` # Dump to stdout
191
191
+
plcbundle mempool dump
192
192
+
193
193
+
# Save to file
194
194
+
plcbundle mempool dump > mempool.jsonl
195
195
+
plcbundle mempool dump -o mempool.jsonl
196
196
+
197
197
+
# Pipe to jq
198
198
+
plcbundle mempool dump | jq -r .did
199
199
+
200
200
+
# Count operations
201
201
+
plcbundle mempool dump | wc -l
202
202
+
203
203
+
# Using alias
204
204
+
plcbundle mempool export`,
205
205
+
206
206
+
RunE: func(cmd *cobra.Command, args []string) error {
207
207
+
mgr, _, err := getManagerFromCommand(cmd, "")
208
208
+
if err != nil {
209
209
+
return err
102
210
}
103
103
-
}
211
211
+
defer mgr.Close()
104
212
105
105
-
fmt.Fprintf(os.Stderr, "Exported %d operations from mempool\n", len(ops))
106
106
-
return nil
213
213
+
ops, err := mgr.GetMempoolOperations()
214
214
+
if err != nil {
215
215
+
return fmt.Errorf("failed to get mempool operations: %w", err)
216
216
+
}
217
217
+
218
218
+
if len(ops) == 0 {
219
219
+
fmt.Fprintf(os.Stderr, "Mempool is empty\n")
220
220
+
return nil
221
221
+
}
222
222
+
223
223
+
// Determine output destination
224
224
+
var output *os.File
225
225
+
if outputFile != "" {
226
226
+
output, err = os.Create(outputFile)
227
227
+
if err != nil {
228
228
+
return fmt.Errorf("failed to create output file: %w", err)
229
229
+
}
230
230
+
defer output.Close()
231
231
+
fmt.Fprintf(os.Stderr, "Exporting to: %s\n", outputFile)
232
232
+
} else {
233
233
+
output = os.Stdout
234
234
+
}
235
235
+
236
236
+
// Write JSONL
237
237
+
for _, op := range ops {
238
238
+
if len(op.RawJSON) > 0 {
239
239
+
output.Write(op.RawJSON)
240
240
+
} else {
241
241
+
data, _ := json.Marshal(op)
242
242
+
output.Write(data)
243
243
+
}
244
244
+
output.Write([]byte("\n"))
245
245
+
}
246
246
+
247
247
+
fmt.Fprintf(os.Stderr, "Exported %d operations from mempool\n", len(ops))
248
248
+
return nil
249
249
+
},
107
250
}
108
251
109
109
-
// Default: Show mempool stats
110
110
-
return showMempoolStats(mgr, dir, *verbose)
252
252
+
cmd.Flags().StringVarP(&outputFile, "output", "o", "", "Output file (default: stdout)")
253
253
+
254
254
+
return cmd
111
255
}
112
256
113
113
-
func showMempoolStats(mgr BundleManager, dir string, verbose bool) error {
257
257
+
// ============================================================================
258
258
+
// HELPER FUNCTIONS
259
259
+
// ============================================================================
260
260
+
261
261
+
func showMempoolStatus(mgr BundleManager, dir string, verbose bool) error {
114
262
stats := mgr.GetMempoolStats()
115
263
count := stats["count"].(int)
116
264
canCreate := stats["can_create_bundle"].(bool)
···
118
266
minTimestamp := stats["min_timestamp"].(time.Time)
119
267
validated := stats["validated"].(bool)
120
268
121
121
-
fmt.Printf("Mempool Status:\n")
122
122
-
fmt.Printf(" Target bundle: %06d\n", targetBundle)
123
123
-
fmt.Printf(" Operations: %d\n", count)
124
124
-
fmt.Printf(" Can create bundle: %v (need %d)\n", canCreate, types.BUNDLE_SIZE)
125
125
-
fmt.Printf(" Min timestamp: %s\n", minTimestamp.Format("2006-01-02 15:04:05"))
269
269
+
fmt.Printf("Mempool Status\n")
270
270
+
fmt.Printf("══════════════\n\n")
271
271
+
fmt.Printf(" Directory: %s\n", dir)
272
272
+
fmt.Printf(" Target bundle: %06d\n", targetBundle)
273
273
+
fmt.Printf(" Operations: %d / %d\n", count, types.BUNDLE_SIZE)
274
274
+
fmt.Printf(" Min timestamp: %s\n\n", minTimestamp.Format("2006-01-02 15:04:05"))
126
275
276
276
+
// Validation status
127
277
validationIcon := "✓"
128
278
if !validated {
129
279
validationIcon = "⚠️"
130
280
}
131
131
-
fmt.Printf(" Validated: %s %v\n", validationIcon, validated)
281
281
+
fmt.Printf(" Validated: %s %v\n", validationIcon, validated)
132
282
133
283
if count > 0 {
284
284
+
// Size information
134
285
if sizeBytes, ok := stats["size_bytes"].(int); ok {
135
135
-
fmt.Printf(" Size: %.2f KB\n", float64(sizeBytes)/1024)
286
286
+
fmt.Printf(" Size: %.2f KB\n", float64(sizeBytes)/1024)
136
287
}
137
288
289
289
+
// Time range
138
290
if firstTime, ok := stats["first_time"].(time.Time); ok {
139
139
-
fmt.Printf(" First operation: %s\n", firstTime.Format("2006-01-02 15:04:05"))
291
291
+
fmt.Printf(" First op: %s\n", firstTime.Format("2006-01-02 15:04:05"))
140
292
}
141
141
-
142
293
if lastTime, ok := stats["last_time"].(time.Time); ok {
143
143
-
fmt.Printf(" Last operation: %s\n", lastTime.Format("2006-01-02 15:04:05"))
294
294
+
fmt.Printf(" Last op: %s\n", lastTime.Format("2006-01-02 15:04:05"))
144
295
}
145
296
297
297
+
fmt.Printf("\n")
298
298
+
299
299
+
// Progress bar
146
300
progress := float64(count) / float64(types.BUNDLE_SIZE) * 100
147
147
-
fmt.Printf(" Progress: %.1f%% (%d/%d)\n", progress, count, types.BUNDLE_SIZE)
301
301
+
fmt.Printf(" Progress: %.1f%% (%d/%d)\n", progress, count, types.BUNDLE_SIZE)
148
302
149
149
-
// Progress bar
150
303
barWidth := 40
151
304
filled := int(float64(barWidth) * float64(count) / float64(types.BUNDLE_SIZE))
152
305
if filled > barWidth {
153
306
filled = barWidth
154
307
}
155
308
bar := strings.Repeat("█", filled) + strings.Repeat("░", barWidth-filled)
156
156
-
fmt.Printf(" [%s]\n", bar)
309
309
+
fmt.Printf(" [%s]\n\n", bar)
310
310
+
311
311
+
// Bundle creation status
312
312
+
if canCreate {
313
313
+
fmt.Printf(" ✓ Ready to create bundle\n")
314
314
+
} else {
315
315
+
remaining := types.BUNDLE_SIZE - count
316
316
+
fmt.Printf(" Need %s more operations\n", formatNumber(remaining))
317
317
+
}
157
318
} else {
158
158
-
fmt.Printf(" (empty)\n")
319
319
+
fmt.Printf("\n (empty)\n")
159
320
}
321
321
+
322
322
+
fmt.Printf("\n")
160
323
161
324
// Verbose: Show sample operations
162
325
if verbose && count > 0 {
163
163
-
fmt.Println()
164
164
-
fmt.Printf("Sample operations (showing up to 10):\n")
326
326
+
fmt.Printf("Sample Operations (first 10)\n")
327
327
+
fmt.Printf("────────────────────────────\n\n")
165
328
166
329
ops, err := mgr.GetMempoolOperations()
167
330
if err != nil {
···
178
341
fmt.Printf(" %d. DID: %s\n", i+1, op.DID)
179
342
fmt.Printf(" CID: %s\n", op.CID)
180
343
fmt.Printf(" Created: %s\n", op.CreatedAt.Format("2006-01-02 15:04:05.000"))
344
344
+
fmt.Printf("\n")
181
345
}
182
346
183
347
if len(ops) > showCount {
184
184
-
fmt.Printf(" ... and %d more\n", len(ops)-showCount)
348
348
+
fmt.Printf(" ... and %d more\n\n", len(ops)-showCount)
185
349
}
186
350
}
187
351
188
188
-
fmt.Println()
189
189
-
190
190
-
// Show mempool file
352
352
+
// Show mempool file location
191
353
mempoolFilename := fmt.Sprintf("plc_mempool_%06d.jsonl", targetBundle)
192
354
fmt.Printf("File: %s\n", filepath.Join(dir, mempoolFilename))
193
355
-1
cmd/plcbundle/commands/server.go
···
1
1
-
// cmd/plcbundle/commands/server.go
2
1
package commands
3
2
4
3
import (
-1
cmd/plcbundle/commands/status.go
···
1
1
-
// repo/cmd/plcbundle/commands/status.go
2
1
package commands
3
2
4
3
import (
-1
cmd/plcbundle/commands/stream.go
···
1
1
-
// repo/cmd/plcbundle/commands/stream.go
2
1
package commands
3
2
4
3
import (
-1
cmd/plcbundle/commands/sync.go
···
1
1
-
// cmd/plcbundle/commands/sync.go
2
1
package commands
3
2
4
3
import (
+2
-2
cmd/plcbundle/main.go
···
64
64
65
65
// Namespaced commands
66
66
cmd.AddCommand(commands.NewDIDCommand())
67
67
-
/*cmd.AddCommand(commands.NewIndexCommand())
67
67
+
//cmd.AddCommand(commands.NewIndexCommand())
68
68
cmd.AddCommand(commands.NewMempoolCommand())
69
69
-
cmd.AddCommand(commands.NewDetectorCommand())
69
69
+
/*cmd.AddCommand(commands.NewDetectorCommand())
70
70
71
71
// Monitoring & maintenance
72
72
cmd.AddCommand(commands.NewWatchCommand())