Skip to content

Commit

Permalink
feat: migrate to jsonb and optimize query
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya committed Nov 14, 2024
1 parent 572b9d8 commit c2c9ca3
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 68 deletions.
81 changes: 32 additions & 49 deletions internal/nostr/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,82 +45,65 @@ type Subscription struct {
EventChan chan *nostr.Event `gorm:"-"`
RequestEvent *RequestEvent `gorm:"-"`

// TODO: fix an elegant solution to store datatypes
IdsString string
KindsString string
AuthorsString string
TagsString string
IdsJson json.RawMessage `gorm:"type:jsonb"`
KindsJson json.RawMessage `gorm:"type:jsonb"`
AuthorsJson json.RawMessage `gorm:"type:jsonb"`
TagsJson json.RawMessage `gorm:"type:jsonb"`
}

func (s *Subscription) BeforeSave(tx *gorm.DB) error {
var err error
if s.Ids != nil {
var idsJson []byte
idsJson, err = json.Marshal(s.Ids)
if err != nil {
return err
}
s.IdsString = string(idsJson)
if s.IdsJson, err = json.Marshal(s.Ids); err != nil {
return err
}
}

if s.Kinds != nil {
var kindsJson []byte
kindsJson, err = json.Marshal(s.Kinds)
if err != nil {
return err
}
s.KindsString = string(kindsJson)
if s.KindsJson, err = json.Marshal(s.Kinds); err != nil {
return err
}
}

if s.Authors != nil {
var authorsJson []byte
authorsJson, err = json.Marshal(s.Authors)
if err != nil {
return err
}
s.AuthorsString = string(authorsJson)
if s.AuthorsJson, err = json.Marshal(s.Authors); err != nil {
return err
}
}

if s.Tags != nil {
var tagsJson []byte
tagsJson, err = json.Marshal(s.Tags)
if err != nil {
return err
}
s.TagsString = string(tagsJson)
if s.TagsJson, err = json.Marshal(s.Tags); err != nil {
return err
}
}

return nil
}

func (s *Subscription) AfterFind(tx *gorm.DB) error {
var err error
if s.IdsString != "" {
err = json.Unmarshal([]byte(s.IdsString), &s.Ids)
if err != nil {
return err
}
if len(s.IdsJson) > 0 {
if err = json.Unmarshal(s.IdsJson, &s.Ids); err != nil {
return err
}
}

if s.KindsString != "" {
err = json.Unmarshal([]byte(s.KindsString), &s.Kinds)
if err != nil {
return err
}
if len(s.KindsJson) > 0 {
if err = json.Unmarshal(s.KindsJson, &s.Kinds); err != nil {
return err
}
}

if s.AuthorsString != "" {
err = json.Unmarshal([]byte(s.AuthorsString), &s.Authors)
if err != nil {
return err
}
if len(s.AuthorsJson) > 0 {
if err = json.Unmarshal(s.AuthorsJson, &s.Authors); err != nil {
return err
}
}

if s.TagsString != "" {
err = json.Unmarshal([]byte(s.TagsString), &s.Tags)
if err != nil {
return err
}
if len(s.TagsJson) > 0 {
if err = json.Unmarshal(s.TagsJson, &s.Tags); err != nil {
return err
}
}

return nil
Expand Down
1 change: 0 additions & 1 deletion internal/nostr/nostr.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ func NewService(ctx context.Context) (*Service, error) {

subscriptions := make(map[string]*nostr.Subscription)

// TODO: Check limits
client := expo.NewPushClient(&expo.ClientConfig{
Host: "https://api.expo.dev",
APIURL: "/v2",
Expand Down
32 changes: 14 additions & 18 deletions internal/nostr/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (svc *Service) NIP47PushNotificationHandler(c echo.Context) error {
}

var existingSubscriptions []Subscription
if err := svc.db.Where("push_token = ? AND open = ?", requestData.PushToken, true).Find(&existingSubscriptions).Error; err != nil {
if err := svc.db.Where("push_token = ? AND open = ? AND authors_json->>0 = ? AND tags_json->'p'->>0 = ?", requestData.PushToken, true, requestData.WalletPubkey, requestData.ConnPubkey).Find(&existingSubscriptions).Error; err != nil {
svc.Logger.WithError(err).WithFields(logrus.Fields{
"push_token": requestData.PushToken,
}).Error("Failed to check existing subscriptions")
Expand All @@ -59,23 +59,19 @@ func (svc *Service) NIP47PushNotificationHandler(c echo.Context) error {
})
}

for _, existingSubscription := range existingSubscriptions {
existingWalletPubkey := (*existingSubscription.Authors)[0]
existingConnPubkey := (*existingSubscription.Tags)["p"][0]

if existingWalletPubkey == requestData.WalletPubkey && existingConnPubkey == requestData.ConnPubkey {
svc.Logger.WithFields(logrus.Fields{
"wallet_pubkey": requestData.WalletPubkey,
"relay_url": requestData.RelayUrl,
"push_token": requestData.PushToken,
}).Debug("Subscription already started")
return c.JSON(http.StatusOK, PushSubscriptionResponse{
SubscriptionId: existingSubscription.Uuid,
PushToken: requestData.PushToken,
WalletPubkey: requestData.WalletPubkey,
AppPubkey: requestData.ConnPubkey,
})
}
if len(existingSubscriptions) > 0 {
existingSubscription := existingSubscriptions[0]
svc.Logger.WithFields(logrus.Fields{
"wallet_pubkey": requestData.WalletPubkey,
"relay_url": requestData.RelayUrl,
"push_token": requestData.PushToken,
}).Debug("Subscription already started")
return c.JSON(http.StatusOK, PushSubscriptionResponse{
SubscriptionId: existingSubscription.Uuid,
PushToken: requestData.PushToken,
WalletPubkey: requestData.WalletPubkey,
AppPubkey: requestData.ConnPubkey,
})
}

svc.Logger.WithFields(logrus.Fields{
Expand Down
143 changes: 143 additions & 0 deletions migrations/202411131742_update_subscriptions_jsonb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package migrations

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

// Update subscriptions table to use JSONB columns for ids, kinds, authors, and tags
var _202411131742_update_subscriptions_jsonb = &gormigrate.Migration{
ID: "202411131742_update_subscriptions_jsonb",
Migrate: func(tx *gorm.DB) error {
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN ids_json jsonb").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN kinds_json jsonb").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN authors_json jsonb").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN tags_json jsonb").Error; err != nil {
return err
}

if err := tx.Exec(`
UPDATE subscriptions
SET ids_json = CASE
WHEN ids_string = '' THEN '[]'::jsonb
ELSE ids_string::jsonb
END
WHERE ids_string IS NOT NULL
`).Error; err != nil {
return err
}

if err := tx.Exec(`
UPDATE subscriptions
SET kinds_json = CASE
WHEN kinds_string = '' THEN '[]'::jsonb
ELSE kinds_string::jsonb
END
WHERE kinds_string IS NOT NULL
`).Error; err != nil {
return err
}

if err := tx.Exec(`
UPDATE subscriptions
SET authors_json = CASE
WHEN authors_string = '' THEN '[]'::jsonb
ELSE authors_string::jsonb
END
WHERE authors_string IS NOT NULL
`).Error; err != nil {
return err
}

if err := tx.Exec(`
UPDATE subscriptions
SET tags_json = CASE
WHEN tags_string = '' THEN '{}'::jsonb
ELSE tags_string::jsonb
END
WHERE tags_string IS NOT NULL
`).Error; err != nil {
return err
}

if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN ids_string").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN kinds_string").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN authors_string").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN tags_string").Error; err != nil {
return err
}

if err := tx.Exec("CREATE INDEX IF NOT EXISTS subscriptions_open ON subscriptions (open)").Error; err != nil {
return err
}
if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_subscriptions_authors_json ON subscriptions USING gin (authors_json)").Error; err != nil {
return err
}
if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_subscriptions_tags_json ON subscriptions USING gin (tags_json)").Error; err != nil {
return err
}

return nil
},
Rollback: func(tx *gorm.DB) error {
if err := tx.Exec("DROP INDEX IF EXISTS idx_subscriptions_authors_json").Error; err != nil {
return err
}
if err := tx.Exec("DROP INDEX IF EXISTS idx_subscriptions_tags_json").Error; err != nil {
return err
}

if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN ids_string TEXT").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN kinds_string TEXT").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN authors_string TEXT").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN tags_string TEXT").Error; err != nil {
return err
}

if err := tx.Exec("UPDATE subscriptions SET ids_string = ids_json::text WHERE ids IS NOT NULL").Error; err != nil {
return err
}
if err := tx.Exec("UPDATE subscriptions SET kinds_string = kinds_json::text WHERE kinds IS NOT NULL").Error; err != nil {
return err
}
if err := tx.Exec("UPDATE subscriptions SET authors_string = authors_json::text WHERE authors IS NOT NULL").Error; err != nil {
return err
}
if err := tx.Exec("UPDATE subscriptions SET tags_string = tags_json::text WHERE tags IS NOT NULL").Error; err != nil {
return err
}

if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN ids_json").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN kinds_json").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN authors_json").Error; err != nil {
return err
}
if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN tags_json").Error; err != nil {
return err
}

return nil
},
}
1 change: 1 addition & 0 deletions migrations/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func Migrate(db *gorm.DB) error {
_202404031539_add_indexes,
_202407171220_add_response_received_at_to_request_events,
_202411071013_add_push_token_and_is_ios_to_subscriptions,
_202411131742_update_subscriptions_jsonb,
})

return m.Migrate()
Expand Down

0 comments on commit c2c9ca3

Please sign in to comment.