Is `[Table].Last()` safe to use? | Race conditions

I often use this pattern:

RunActions(
  [Table].AddRow(),
  [Table 2].Filter([Selected?]).ModifyRows([Associated], [Table].Last())
)

But I’m worried about race conditions.

What I’m trying to do is this:

RunActions(,
  [Table 2].Filter([Selected?]).ModifyRows([Associated], [Table].AddRow())
)

I.e. add a row to [Table] and associate it with [Table 2]. I can’t do this because sometimes it returns an Action instead of a Row. I’ve been told not to take that approach.

However, I have a concern with the [Table].Last() approach.

Is it possible that someone else could add a new row at just the right time while my button is executing and I could accidentally grab the wrong row when I call Last()?

@Jono_Bouwmeester @BenLee @Bill_French @Nina_Ledid do you know the answer to this?

One possible way to test:

RunActions(
  [Table].AddRow([Name], "Correct"),
  _Delay(true, 100000),
  [Table 2].Filter([Selected?]).ModifyRows([Associated], [Table].Last())
)

then a different user clicks a button with

[Table].AddRow([Name], "Incorrect")

If “Incorrect” ever showed up in [Table] we would have the risk of a race condition

Yes (almost certainly).

I believe this is also true, and while I admire this problem, I have no remedy that comes to mind. Thinking…

@Connor_McCormick1

the action AddRow() returns a link to the new row and there are no race conditions

so, for example, TableA.thisRow(NewRecord, TableB.AddRow( ..... ))

will add a new row to TableB and save a link to it in the in TableA.NewRecord column

I use this idiom to record a link to the new row OR to create linked lists and double-linked lists etc

max

1 Like

Well, did you test it?

I generally assume this may lead to race conditions. I’ve never tested it myself but even if it’s safe now, it’s not guaranteed to stay safe as Coda updates. I only use this .Last() approach in docs that aren’t meant to have much concurrency. If I know that multiple people will use it at the same time, I do what Max says and always store the freshly created row in some temporary cell somewhere.

I get that this is the elegant way to do it, but it doesn’t always work.

TableA.AddRow(NewRecord, TableB.AddRow( ..... ))

For example, try adding a row and modifying a lookup simultaneously:

Paul! I’ve been missing you, wishing for your safety.

No, I haven’t tested it I don’t have any friends :sob:

uhh two-three separate Coda accounts? :smiley: I have like 3 or 4 to test multi-user behavior lol :slight_smile:

Anyway, I could be your friend :slight_smile: Do you wanna me to help you test it or should I test it all at my side?

Always wanted to be your friend.

Sure! Send me a doc I can click buttons like you wouldn’t believe

1 Like

I thought you had the doc already? You could add me there to click the button while you’re waiting for that Delay(10000)

Ah well, it turns out I didn’t even need friends:

It’s a race condition. Damn that’s a huge bummer.

Here I was, all ready to recreate your post in a doc trying to play around with it, when I realized you had already done that in your doc. Thanks for that, I appreciate you taking the time to share your learnings with all of us!

So now that you figured out that race conditions do apply, does that mean you will keep using the [Table].Last() approach anyway?

Or are you planning to revert back to your “I’d prefer it this way but was told it’s incorrect”-way of doing it as you layed out?

Well, if you go this page and hit the Nested Approach button twice, you can see that it just doesn’t work right. We’re somewhat forced to use the Last() Approach

Unless someone can find a brilliant workaround

maybe the brilliant workaround is to store a RowNumber integer in every row upon row-creation

create the RowNumber column
set it to number type
select the Number Options
and set the Value for New Rows to this formula

Count(thisTable)+1

and then you have a stable row-number that resists filters and sorts

much better than using Last()

max

2 Likes

This is pretty clever.

The issue is that in using it you’ll have to compute the current row count and keep it in a WithName.

E.g.

RunActions(
  [Table Add].Count().WithName(BeforeRowAddCount,
    [Table Add].AddRow(),
    TargetRow.ModifyRows([Table Modify].[Lookup], 
      TargetRow.[Lookup].Splice(0, 0, [Table Add].Filter([Row Number] = BeforeRowAddCount+1).First())
    )
  )
)

This will work! Its downsides are that:

  1. You have the added variable to keep track of
  2. If you ever added multiple rows and wanted to switch the order they were added, you’d have to remember to change the later part from BeforeRowAddCount + N to BeforeRowAddCount + M
  3. You need to set up a column that counts row number, which is one more thing your formula relies on (more fragile context)
  4. To anyone who doesn’t know Coda deeply, this is just mysterious

How elegant would this be in comparison?

RunActions(
  TargetRow.ModifyRows([Table Modify].[Lookup], 
    TargetRow.[Lookup].Splice(0, 0, [Table Add].AddRow())
  )
)

Maybe if we had an Add() or Append() formula it could work? Not sure why Splice does this.