# [CommitsAlone] — a function whose writes land by themselves

> A function marked `[CommitsAlone]` runs in a unit of work of its own. What it writes is committed when it returns and stays committed whatever its caller does next — fail, discard, or carry on. A fault inside it discards only its own writes. It neither sees nor commits the caller's pending changes, so it can be called in the middle of other work and from a task. Use it for the rows that must outlive a failure: a usage meter, a reservation other callers have to see at once, an attempt log.

<!-- id: function-commits-alone · area: function · stability: stable · html: https://osysharp.com/reference/function/commits-alone/ -->

## Summary        {#summary}
A function's writes normally join its caller's [unit of work](https://osysharp.com/reference/function/unit-of-work/): they land when the caller's work
is committed, and are thrown away with it when the caller fails. `[CommitsAlone]` is for the writes that must not
follow the caller:

```osy title="the attempt is on record even though the import failed" test app=function-commits-alone
entity Attempt {
  [Required, MaxLength(200)] string What;
  security { allow create, read when IsAuthenticated || IsAnonymous; }
}

entity Customer {
  [Required, MaxLength(200)] string Name;
  security { allow create, read when IsAuthenticated || IsAnonymous; }
}

[CommitsAlone]
void NoteAttempt(string what) { new Attempt { What = what }; }

void Import(string name) {
  var customer = new Customer { Name = name };
  NoteAttempt("import " + name);                     // committed here, by itself
  if (name == "") { throw new ValidationException("A customer needs a name."); }
}
```

## Signature      {#signature}
```osy syntax
[CommitsAlone]
<Type> <Name>(<value parameters>) { … }      // a top-level function; values in, a value out
```

## Description    {#description}

### What happens at the call?   {#the-call}
The function gets a fresh, empty unit of work. Its body runs in it, as the caller — the same person, the same rules.
When it returns, that unit of work is committed, atomically, before the caller's next statement runs.

| | the function's writes | the caller's pending writes |
|---|---|---|
| the function returns | committed, at once | untouched — still pending |
| the function throws | discarded | untouched; the caller may `catch` and go on |
| the caller later fails or calls `UnitOfWork.Discard()` | **still committed** | discarded |
| the caller later commits | already committed | committed |

### What does it see?   {#what-it-sees}
The database as it is stored. It does **not** see rows its caller created or changed and has not committed, and it
never commits them. Once it has returned, the caller sees what it wrote like any other stored row — and that
includes a row the caller already holds: a variable read before the call reads the stored value after it. A change of
the caller's own that is still pending on that row is kept, on top of what was stored.

### Why values, and not rows?   {#values}
A row belongs to the unit of work that read it. So the function takes and returns values — pass a row's `Id` and read
the row inside. A row-typed parameter or return is refused when you compile, saying so.

```osy title="hand it the id, read the row inside" test app=function-commits-alone
entity Tally {
  [Unique, MaxLength(40)] string Name;
  int Count = 0;
  security { allow create, read, update when IsAuthenticated || IsAnonymous; }
}

[CommitsAlone]
int CountOne(Guid tallyId) {
  var tally = Tally.Single(t => t.Id == tallyId);
  tally.Count = tally.Count + 1;
  return tally.Count;
}
```

### Can a task call it?   {#tasks}
Yes — and it is the way a task writes. A [[function-async-await#writes|task may not write]] to its caller's unit of
work, because the two would race; a function with a unit of work of its own has nothing to race over. Each call
commits by itself, so calls running at the same time are separate transactions: guard a value two of them may change
with [[entity-concurrency-check|`[ConcurrencyCheck]`]] and retry the loser.

```osy title="one row per item, written from the loop's tasks" test app=function-commits-alone
void NoteAll(List<string> items) {
  Parallel.ForEach(items, item => NoteAttempt("seen " + item));
}
```

### Does `except via` still apply?   {#except-via}
Unchanged. A function a [`deny … except via`](https://osysharp.com/reference/security/except-via/) names may also be `[CommitsAlone]`: its own
statements write past that deny, a helper's do not, and the caller still needs the grant.

### What about a workflow body?   {#durable}
It works there, as the kit's or the app's code that wrote it. Treat the call as you would an outbound call: a run that
is interrupted after the function committed and before the run recorded its own progress calls it again when it
resumes. Where once matters, give the row a `[Unique]` key the second call collides on.

An event the function raises is committed with it, and its handlers run when the body's **step** has ended, with the
step's other events — not when the function returns ([[function-raise#in-a-step]]).

### What is refused?   {#refused}
| you wrote | what the compiler says |
|---|---|
| a row parameter, a row return, or a list of rows | a row belongs to the unit of work that read it — take or return its `Id` |
| `async`, or a `Task` return | it commits when it returns, so it must have finished by then |
| a `stream<T>` producer | a producer has no single return to commit at |
| a class's method | a unit of work of its own belongs to a top-level function |
| `[Client]` beside it | it runs on the server, where the commit happens — the attribute places it there, so write no side |
| `[OwnUnitOfWork]`, its former name | `[OwnUnitOfWork]` is now `[CommitsAlone]` — the same meaning under the new name |

At run time, a function that has to wait part-way — a dialog in the browser, a workflow, a durable delay — is refused
with the same reason: do the waiting in the caller and hand the function what to write.

## Examples       {#examples}
The rows are there after the failure, proved by a test:

```osy title="what survives the caller's failure" run app=function-commits-alone
[Test]
void The_attempt_outlives_the_failed_import() {
  Assert.Throws<ValidationException>(() => Import(""));
  Assert.Equal(1, Attempt.Count());          // NoteAttempt's row is stored
  Assert.Equal(0, Customer.Count());         // the import's own row went with the failure
}

[Test]
void A_counter_is_changed_by_id() {
  var tally = new Tally { Name = "visits" };
  UnitOfWork.Commit();
  Assert.Equal(1, CountOne(tally.Id));
  Assert.Equal(2, CountOne(tally.Id));
}

[Test]
void A_row_the_caller_holds_reads_what_the_function_stored() {
  var tally = new Tally { Name = "held" };
  UnitOfWork.Commit();
  CountOne(tally.Id);
  Assert.Equal(1, tally.Count);                                        // the variable from before the call
  Assert.Equal(1, Tally.Single(t => t.Name == "held").Count);          // …and the row read again
}

[Test]
void A_change_the_caller_has_pending_on_that_row_is_kept() {
  var tally = new Tally { Name = "pending" };
  UnitOfWork.Commit();
  tally.Name = "renamed";                    // the caller's own, not yet saved
  CountOne(tally.Id);
  Assert.Equal("renamed", tally.Name);
  Assert.Equal(1, tally.Count);
  UnitOfWork.Commit();
  Assert.Equal(1, Tally.Count(t => t.Name == "renamed" && t.Count == 1));
}

[Test]
void A_loop_of_tasks_writes_one_row_each() {
  NoteAll(["a", "b", "c"]);
  Assert.Equal(3, Attempt.Count());
}
```

## See also       {#see-also}
- [UnitOfWork](https://osysharp.com/reference/function/unit-of-work/) — the unit of work every other function shares with its caller
- [try / catch / finally](https://osysharp.com/reference/function/try-catch/) — what a caught fault keeps and discards
- [async / await and Task](https://osysharp.com/reference/function/async-await/) — tasks, and why they do not write their caller's rows
- [deny … except via](https://osysharp.com/reference/security/except-via/) — a row only one function may write
- [ConcurrencyCheck](https://osysharp.com/reference/entity/concurrency-check/) — two writers of one value
