Let's actually macroexpand the two implementations and see how they differ:
* (macroexpand '(once-only (foo bar) (+ foo bar)))
(LET ((#:G619 (GENSYM)) (#:G620 (GENSYM)))
`(LET ((,#:G619 ,FOO) (,#:G620 ,BAR))
,(LET ((FOO #:G619) (BAR #:G620))
(+ FOO BAR))))
* (macroexpand '(my-once-only (foo bar) (+ foo bar)))
(LIST 'LET (LIST (LIST '#:G621 FOO) (LIST '#:G622 BAR))
(LET ((FOO '#:G621) (BAR '#:G622))
(+ FOO BAR)))
Let's rewrite your macroexpansion to something easier for a Lisper to read:
`(LET ((#:G621 ,FOO) (#:G622 ,BAR))
,(LET ((FOO '#:G621) (BAR '#:G622))
(+ FOO BAR)))
Notice that your version lacks the indirection with the additional gensym
. That means each invocation of your outer macro (the one that is using my-once-only
) is using the same symbols each time. If your macro calls nest (e.g., you use your outer macro inside the body of another use of the outer macro), the symbols will collide.