diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-11-30 15:14:14 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-12-03 10:19:07 +0300 |
commit | 1a1728782fdf444ec55ba5d968fe6845adc1bd28 (patch) | |
tree | 77e9a814cc083ab9b9b220bd30f4c04734985068 /doc | |
parent | dee973f98c485bc70c6d73cec1a49c498ac22db0 (diff) |
doc: Document reference-transaction hook
The hooks documentation does not document the reference-transaction
hook. Add this missing information.
Diffstat (limited to 'doc')
-rw-r--r-- | doc/hooks.md | 99 |
1 files changed, 82 insertions, 17 deletions
diff --git a/doc/hooks.md b/doc/hooks.md index 493894e30..296683f6e 100644 --- a/doc/hooks.md +++ b/doc/hooks.md @@ -23,14 +23,14 @@ able to derive required information: script, which locates the `gitaly-hooks` executable using `GITALY_BIN_DIR`. - We inject `GITALY_HOOKS_PAYLOAD`, which contains JSON-formatted data. This payload encodes various information: - - Which hooks have been requested. Gitaly uses this to only configure a - subset of hooks even if the executed Git command would run additional - hooks. - - Information about the repository the hook is executed in. - - Information about how to connect to Gitaly. - - Information about the ongoing transaction, if any. - - Information about the user who triggered hook. This is required for access - checks. + - Which hooks have been requested. Gitaly uses this to only configure a + subset of hooks even if the executed Git command would run additional + hooks. + - Information about the repository the hook is executed in. + - Information about how to connect to Gitaly. + - Information about the ongoing transaction, if any. + - Information about the user who triggered the hook. This is required for + access checks. The `gitaly-hooks` executable is only a gateway to be able to use gRPC and execute the hook logic inside of the Gitaly server process. It uses the injected @@ -50,13 +50,54 @@ execution path is: The logic implemented in Gitaly's hook RPC handlers depends on which hook is being executed. +### Reference-transaction hook + +The reference-transaction hook is executed whenever Git updates a reference from +an old value to a new value, where it hooks into the low-level mechanism to +update references in Git. An update of a hook goes through two phases, where the +hook is executed once for each phase: + +1. Git "prepares" the reference transaction. When the hook is called in this + phase, the references that are about to be updated are locked for concurrent + modification. +1. Git "commits" or "aborts" the reference transaction. + +Depending on how the command updates references, this hook can be executed per +reference that is about to be updated, or for all references at once. The +references that are about to be updated are received on standard input. + +In Gitaly, we use the reference-transaction mechanism to perform votes on all +references which are updated by Git in a way that is transparent to Gitaly +itself. That is, we do not know how often and with which arguments the hook is +going to be executed when we execute any Git command, and the order can change +when new Git versions are rolled out that do internal changes. + +The reference-transaction can therefore be used to put an absolute ordering on +all reference updates across multiple Gitaly nodes. To ensure that multiple +Gitaly nodes part of a single transaction (that is, all Gitalies execute the +same logic at the same point in time) perform the same action, we vote on each +of the hook executions. Gitaly nodes: + +1. Take the standard input containing all references we are about to update. +1. Hash these references. +1. Use the resulting hash as it's vote that it sends to Praefect. + +We expect the hash must be the same across all Gitaly nodes if (and only if) all +nodes: + +- Are in the same state before. +- Make the change deterministically. + +If a [quorum](design_ha.md) is reached, the update is committed to disk, +otherwise the update is rejected. + ### Receive-Pack Hooks These hooks are executed by `git-receive-pack` whenever changes are pushed into a repository and can be used to: - Verify whether the changes should be allowed into the repository in the first - place (`pre-receive` and `update` hooks) + place (`pre-receive` and `update` hooks). - Show information to the user (`post-update` hook). While the reference-transaction hook executes on all Gitaly nodes, the @@ -64,16 +105,17 @@ receive-pack style hooks only run on a single Gitaly, which is the primary node. It can thus happen that receive-pack style hooks reject the update on the primary node, which is not something the secondary nodes would see. The result is that secondaries would now hang until the timeout for the RPC call triggers -given that the primary is never going to vote on this transaction. To fix the -hang, transactions support graceful stops: if the primary node fails the RPC -call in code that is only executed on the primary, then it will stop the +given that the primary is never going to vote on this transaction. + +To fix the hang, transactions support graceful stops: if the primary node fails +the RPC call in code that is only executed on the primary, then it will stop the transaction and thus tell other Gitaly nodes to stop waiting for quorum. There are two users of these hooks: - `PostReceivePack` and `SSHReceivePack` directly invoke `git-receive-pack`, which then executes the hooks for us. -- Most RPCs in the OperationService that write objects into the repository +- Most RPCs in the `OperationService` that write objects into the repository manually invoke these hooks using the `updateref.UpdaterWithHooks` structure. These hooks perform the following functions: @@ -91,7 +133,7 @@ These hooks perform the following functions: quarantine directory using the repository's alternative object directory fields so that any subsequent RPC calls that check the change can access those objects. When the access checks succeed, any existing custom pre-receive hooks - installed by the admin are executed. + installed by the administrator are executed. - `update`: The update hook runs after the pre-receive hook at the point where objects from the object quarantine directory have already been migrated into the main repository. This hook only executes custom hooks installed by the @@ -109,9 +151,8 @@ happening. Both custom hooks and the `/internal/allowed` API endpoint may return specially-formatted error messages that have either a `GitLab:` or a `GL-Hook-Error:` prefix. Messages with this prefix are returned as-is to Rails -and signify an error message that ought to be printed to the user directly, -either through the user interface or through standard error of -`git-receive-pack`. +and signify an error message that should be printed to the user directly, either +through the user interface or through standard error of `git-receive-pack`. #### Custom Hooks @@ -145,6 +186,20 @@ sequenceDiagram Custom Hooks->>-Hook Service: OK Hook Service->>-gitaly-hooks: OK gitaly-hooks->>-git-receive-pack: OK + + git-receive-pack->>+gitaly-hooks: exec "reference-transaction prepare" + gitaly-hooks->>+Hook Service: call ReferenceTransactionHook(prepare) + Hook Service->>+Praefect: cast vote + Praefect->>-Hook Service: OK + Hook Service->>-gitaly-hooks: OK + gitaly-hooks->>-git-receive-pack: OK + + git-receive-pack->>+gitaly-hooks: exec "reference-transaction commit" + gitaly-hooks->>+Hook Service: call ReferenceTransaction(commit) + Hook Service->>+Praefect: cast vote + Praefect->>-Hook Service: OK + Hook Service->>-gitaly-hooks: OK + gitaly-hooks->>-git-receive-pack: OK end git-receive-pack->>+gitaly-hooks: exec "post-receive" @@ -174,6 +229,16 @@ sequenceDiagram Custom Hooks->>-Hook Service: OK Hook Service->>-Operation Service: OK + Operation Service->>+Hook Service: call ReferenceTransactionHook(prepare) + Hook Service->>+Praefect: cast vote + Praefect->>-Hook Service: OK + Hook Service->>-Operation Service: OK + + Operation Service->>+Hook Service: call ReferenceTransactionHook(commit) + Hook Service->>+Praefect: cast vote + Praefect->>-Hook Service: OK + Hook Service->>-Operation Service: OK + Operation Service->>+Hook Service: call PostReceiveHook Hook Service->>Rails: decrement counter Hook Service->>+Custom Hooks: exec "post-receive" |