public CompletableFuture<Void> canceledAt(byte[] user, Instant canceledAt) {
    checkUserLength(user);

    UpdateItemRequest request = UpdateItemRequest.builder()
        .tableName(table)
        .key(Map.of(KEY_USER, b(user)))
        .returnValues(ReturnValue.NONE)
        .updateExpression("SET "
            + "#accessed_at = :accessed_at, "
            + "#canceled_at = :canceled_at "
            + "REMOVE #subscription_id")
        .expressionAttributeNames(Map.of(
            "#accessed_at", KEY_ACCESSED_AT,
            "#canceled_at", KEY_CANCELED_AT,
            "#subscription_id", KEY_SUBSCRIPTION_ID))
        .expressionAttributeValues(Map.of(
            ":accessed_at", n(canceledAt.getEpochSecond()),
            ":canceled_at", n(canceledAt.getEpochSecond())))
        .build();
    return client.updateItem(request).thenApply(updateItemResponse -> null);
  }
@Test
  void testCanceledAt() {
    Instant canceled = Instant.ofEpochSecond(NOW_EPOCH_SECONDS + 42);
    assertThat(subscriptions.create(user, password, created)).succeedsWithin(DEFAULT_TIMEOUT);
    assertThat(subscriptions.canceledAt(user, canceled)).succeedsWithin(DEFAULT_TIMEOUT);
    assertThat(subscriptions.get(user, password)).succeedsWithin(DEFAULT_TIMEOUT).satisfies(getResult -> {
      assertThat(getResult).isNotNull();
      assertThat(getResult.type).isEqualTo(FOUND);
      assertThat(getResult.record).isNotNull().satisfies(record -> {
        assertThat(record.accessedAt).isEqualTo(canceled);
        assertThat(record.canceledAt).isEqualTo(canceled);
        assertThat(record.subscriptionId).isNull();
      });
    });
  }