Skip to content
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

Problem with Nested relations saving the same document #537

Open
pavelivanov opened this issue Aug 2, 2016 · 7 comments
Open

Problem with Nested relations saving the same document #537

pavelivanov opened this issue Aug 2, 2016 · 7 comments

Comments

@pavelivanov
Copy link

Hi there! I need ur help. I'm trying to create game statistic, and I wrote simple models (Games, Teams, Rounds, Score) and these relations:

Game.hasAndBelongsToMany(Team, 'teams', 'id', 'id')
Game.hasMany(Round, 'rounds', 'id', 'game_id')
Round.hasMany(Score, 'score', 'id', 'round_id')
Team.hasMany(Score, 'score', 'id', 'team_id')

And in api I do this:

  const game      = new Game({ ... })
  const teams     = req.body.teams.map(() => new Team({ ... }))
  const reounds   = req.body.rounds.map(() => {
                      const round = new Round({ ... })
                      round.score = round.score.map(() => {
                        const score = new Score({ ... })
                        teams[index].score.push(score)
                        return score
                      })
                      return round
                    })

  game.teams = teams
  game.reounds = reounds

  game.saveAll({ teams: { score: true }, rounds: { score: true } })

Everything saving, but there is a problem in Score records (team_id missed sometimes):

[
  {
    "id": "3d9131cc-8f6f-4ba2-a147-2b9e216ea5c1",
    "round_id": "b9c1471a-70e1-492a-9f9d-d9fba4afe7c4",
    "score": 6
  },
  {
    "id": "4949756f-9a85-4d6a-8d5a-150c7c31639a",
    "round_id": "1eda126b-ee72-4598-a946-af2b63298f59",
    "score": 1,
    "team_id": "81aa1618-4f77-424c-a9bf-e62c4077a4a2"
  },
  {
    "id": "d0ab2cc1-7c1f-459c-bd0f-1f32072fd616",
    "round_id": "b9c1471a-70e1-492a-9f9d-d9fba4afe7c4",
    "score": 12
  },
  {
    "id": "06737e74-b374-4791-abdd-28843c7519d8",
    "round_id": "494d141d-ac1f-405e-bc1e-aa3154f55cde",
    "score": 4
  },
  {
    "id": "37841d0a-d07d-4b71-a2af-184b8e5918db",
    "round_id": "84298436-a9aa-427f-baf5-1ad3a24358f0",
    "score": 1,
    "team_id": "f8f5dce5-7d75-4cd3-a937-02817f259e3c"
  }
]
@pavelivanov pavelivanov changed the title Nested relations Problem with Nested relations Aug 2, 2016
@neumino
Copy link
Owner

neumino commented Aug 3, 2016

Can you provide a full script that I can run that reproduce this error?

@pavelivanov
Copy link
Author

Is it ok? Or u'd prefer full working example ?)

const Game = thinky.createModel('Game', {
  id: String,
  name: String,
  created: {
    _type: Date,
    default: r.now()
  },
})

Game.ensureIndex('created')
const Team = thinky.createModel('Team', {
  id: String,
  name: String,
})

Team.ensureIndex('name')
const Round = thinky.createModel('Round', {
  id: String,
  index: Number,
})

Round.ensureIndex('index')
const Score = thinky.createModel('Score', {
  id: String,
  score: Number,
})

Score.ensureIndex('score')
export const add = (req, res) => {
  const game = new Game({ name: 'Game 1' })

  const team_1  = new Team({ name: 'Team 1' })
  const team_2  = new Team({ name: 'Team 2' })
  const teams   = [ team_1, team_2 ]

  const round_1 = new Round({ index: 0 })
  const round_2 = new Round({ index: 1 })
  const rounds  = [ round_1, round_2 ]

  const round_1_score_1 = new Score({ score: 5 })
  const round_1_score_2 = new Score({ score: 2 })
  const round_2_score_1 = new Score({ score: 1 })
  const round_2_score_2 = new Score({ score: 3 })

  team_1.score = [ round_1_score_1, round_2_score_1 ]
  team_2.score = [ round_1_score_2, round_2_score_2 ]

  round_1.score = [ round_1_score_1, round_1_score_2 ]
  round_2.score = [ round_2_score_1, round_2_score_2 ]

  game.teams = teams
  game.rounds = rounds

  game
    .saveAll({ teams: { score: true }, rounds: { score: true } })
    .then(() => {
      res.json({ success: true })
    })
    .error((error) => {
      res.json({ message: error })
    })
}

@pavelivanov
Copy link
Author

@neumino hi! sry for disturbing) do u have any ideas?

@neumino
Copy link
Owner

neumino commented Aug 7, 2016

Can you provide the relations between your model?

@neumino
Copy link
Owner

neumino commented Aug 7, 2016

Never mind, they are in the first message.

@neumino
Copy link
Owner

neumino commented Aug 7, 2016

Ok I see what's happening. It's a bit tricky, but when you do game.saveAll, this is what happens:

  • thinky finds teams and scores documents that it has to save and will save them in parallel.
  • For each of these team/score, it finds a score to save, it happens that here the score are the same, so thinky save them twice (once via team and once via score).
  • The order in which the scores are saved is not deterministic. So one save operation may overwrite the other one.

This is a work around for now:

var thinky = require('./lib/thinky.js')();
var r = thinky.r;

const Game = thinky.createModel('Game', {
  id: String,
  name: String,
  created: {
    _type: Date,
    default: r.now()
  },
})

Game.ensureIndex('created')

const Team = thinky.createModel('Team', {
  id: String,
  name: String,
})

Team.ensureIndex('name')

const Round = thinky.createModel('Round', {
  id: String,
  index: Number,
})

Round.ensureIndex('index')

const Score = thinky.createModel('Score', {
  id: String,
  score: Number,
})

Score.ensureIndex('score')

Game.hasAndBelongsToMany(Team, 'teams', 'id', 'id')
Game.hasMany(Round, 'rounds', 'id', 'game_id')
Round.hasMany(Score, 'score', 'id', 'round_id')
Team.hasMany(Score, 'score', 'id', 'team_id')

const game = new Game({ name: 'Game 1' })

const team_1  = new Team({ name: 'Team 1' })
const team_2  = new Team({ name: 'Team 2' })
const teams   = [ team_1, team_2 ]

const round_1 = new Round({ index: 0 })
const round_2 = new Round({ index: 1 })
const rounds  = [ round_1, round_2 ]

const round_1_score_1 = new Score({ score: 5 })
const round_1_score_2 = new Score({ score: 2 })
const round_2_score_1 = new Score({ score: 1 })
const round_2_score_2 = new Score({ score: 3 })

team_1.score = [ round_1_score_1, round_2_score_1 ]
team_2.score = [ round_1_score_2, round_2_score_2 ]

round_1.score = [ round_1_score_1, round_1_score_2 ]
round_2.score = [ round_2_score_1, round_2_score_2 ]

game.teams = teams
game.rounds = rounds

game
    .saveAll({
      teams: {
        score: true
      }
    }).then(() => {
      console.log('-- save success');
      return game.saveAll({
      rounds: {
        score: true
      },
    })
    }).then(() => {
      return Game.get(game.id).getJoin({
      teams: {
        score: true
      },
      rounds: {
        score: true
      }
      }).run()
    }).then(() => {
      console.log('-- retrieve success');
      console.log(JSON.stringify(game, null, 2));
    }).error((error) => {
      console.log('error', JSON.stringify(error, null, 2));
    })

As for the real fix, there are a few ways to fix that:

  • Don't save things in parallel -- but that's slowish
  • Make documents being saved in a given operation and make another save operation wait for the previous one -- that's as fast as today but we still have multiple writes for the same document
  • Find all the documents to save, find duplicate save, merge them and save everything -- that's the best solution, but requires more work

@neumino neumino changed the title Problem with Nested relations Problem with Nested relations saving the same document Aug 7, 2016
@pavelivanov
Copy link
Author

@neumino I see! thank you for explanation!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants