-
-
Notifications
You must be signed in to change notification settings - Fork 366
replace Map with sync.Map to handle concurrent reads/writes (changes generated by Cursor); #2646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary:
This PR replaces standard Go maps with sync.Map in the telemetry server to handle concurrent reads/writes. The changes affect the data models and initialization code in the server handlers. The goal is to prevent potential race conditions that could occur with regular maps in a concurrent environment.
Review Summary:
While using sync.Map is a good approach for concurrent access, there are significant implementation issues that need to be addressed. The most critical problem is that Go's standard JSON encoder doesn't properly support sync.Map, which could lead to data loss or corruption. Additionally, there are type safety concerns and incomplete map conversion code in some methods.
Please let me know if you have any questions about implementing these fixes - I'd be happy to provide more guidance!
Follow-up suggestions:
@devloai implement custom JSON marshaling/unmarshaling for the structs using sync.Map
@@ -37,7 +38,9 @@ func RouteEvents(router *mux.Router, opts *cli.Options, dbConn persistence.Conne | |||
// https://stackoverflow.com/a/26212073 | |||
router.HandleFunc("/api/v2/events/", func(w http.ResponseWriter, r *http.Request) { | |||
// parse given json data into a new record | |||
logrec := persistence.EventTelemetryRecordV2{} | |||
logrec := persistence.EventTelemetryRecordV2{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sync.Map initialization is correct, but there should be proper error handling for JSON deserialization of sync.Map fields. If the incoming JSON contains data intended for EventArgs, the current code might silently ignore it since the unmarshalled data may not properly map to sync.Map. Consider adding custom JSON unmarshalling for this struct or validate that EventArgs is correctly populated after decoding.
} | ||
|
||
func (logrec ScriptTelemetryRecordV2) PrintRecordInfo(logger *cli.Logger, message string) { | ||
// Convert sync.Map to regular map for printing | ||
results := make(map[string]interface{}) | ||
logrec.CommandResults.Range(func(key, value interface{}) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a potential type safety issue here. The original code has explicit type assertions when converting to a regular map, but the current implementation might assign different types to the results map. The value type should be explicitly checked like it is in the ScriptTelemetryRecordV1.PrintRecordInfo method (lines 46-54). Consider adding proper type assertion for the value parameter similar to:
logrec.CommandResults.Range(func(key, value interface{}) bool {
if k, ok := key.(string); ok {
if v, ok := value.(interface{}); ok {
results[k] = v
}
}
return true
})
BundleName string `json:"commandbundle" bson:"commandbundle" valid:"-"` | ||
ExtensionName string `json:"commandextension" bson:"commandextension" valid:"-"` | ||
ResultCode int `json:"resultcode" bson:"resultcode" valid:"numeric~Invalid result code"` | ||
CommandResults sync.Map `json:"commandresults" bson:"commandresults" valid:"-"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using sync.Map with JSON marshaling/unmarshaling may cause issues with serialization. The default Go JSON encoder doesn't have built-in support for sync.Map. You'll need to implement custom MarshalJSON and UnmarshalJSON methods for each struct using sync.Map to properly serialize and deserialize the data. Without this, data might be lost or improperly stored during JSON operations.
TimeStamp string `json:"timestamp" bson:"timestamp" valid:"rfc3339~Invalid timestamp"` | ||
HandlerId string `json:"handler_id" bson:"handler_id" valid:"-"` | ||
EventType string `json:"type" bson:"type" valid:"-"` | ||
EventArgs sync.Map `json:"args" bson:"args" valid:"-"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The EventTelemetryRecordV2 PrintRecordInfo method (line 204) doesn't handle the EventArgs sync.Map conversion before printing. Add code similar to what's done in ScriptTelemetryRecordV2.PrintRecordInfo to convert the sync.Map to a regular map before printing, otherwise the logging output may be incorrect or misleading.
…generated by Cursor);
Thanks Jared, |
Setup with Docker:
Setup without Docker:
After setup:
If you want to see the event records hit the database:
let me know if you get stuck. |
Replace Map with sync.Map to handle concurrent reads/writes
Description
replace Map with sync.Map to handle concurrent reads/writes
Checklist
Before submitting your pull request, ensure the following requirements are met:
Related Issues
If applicable, link the issues resolved by this pull request:
Additional Notes
These code changes were generated by Cursor AI.
I don't know Go that well, but these changes seem to work in local testing.
Thank you for contributing to pyRevit! 🎉