Synchronous replication on push

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Suppose I have a front end repository:

user@xxxxxxxxxxxxxxxxxxxx:/repo.git

Whenever I push anything to it, I want the push -- that is, all the
objects, and all the ref updates -- to be synchronously replicated to
another remote repository, the back end:

git@xxxxxxxxxxxxxxxxxxx:/repo.git

If this replication fails -- whether because the back end is down, or
because the front end crashed and rolled back to an earlier state, or
because the back end has been updated independently and rejects a
force push, or whatever -- I want the push to fail.  But, absent these
failures, I want frontend and backend to store the same set of objects
and refs.

(Actually, I want to replicate it to a quorum of multiple back ends
with a three-phase commit protocol -- but I'll start with the
single-replica case for simplicity.)

How can I do this with git?


One option, of course, is to use a replicated file system like
glusterfs, or replicated block store like DRBD.  But that

(a) likely requires a lot more round-trips than git push/send-pack,
(b) can't be used for replication to other git hosts like Github, and
(c) can't be used for other remote transports like git-cinnabar.

So I'd like to do this at the git level, not at the file system or
block store level.


Here are some approaches I've tried:

1. `git clone --mirror -o backend git@xxxxxxxxxxxxxxxxxxx:/repo.git'
   to create the front end repository, plus the following pre-receive
   hook in the front end:

	#!/bin/sh
	exec git push backend

   This doesn't work because the pre-receive hook runs in the
   quarantine environment, and `git push' wants to update
   `refs/heads/main', which is forbidden in the quarantine
   environment.

   (However, git push to frontend doesn't actually fail with nonzero
   exit status -- it prints an error message, `ref updates forbidden
   inside quarantine environment', but exits wtih status 0.)

   But maybe the ref update is harmless in this environment.

2. Same as (1), but the pre-receive hook is:

	#!/bin/sh
	unset GIT_QUARANTINE_PATH
	exec git push backend

   This doesn't work because `git push' in the pre-receive hook
   doesn't find anything it needs to push -- the ref update hasn't
   happened yet.

3. Same as (1), but the pre-receive hook assembles a command line of

	exec git push backend ${new0}:${ref0} ${new1}:${ref1} ...,

   with all the ref updates passed on stdin (ignoring the old values).

   This fails because `--mirror can't be combined with refspecs'.

4. Same as (3), but remote.backend.mirror is explicitly disabled after
   `git clone --mirror' finishes.

   On push to the primary, this prints an error message

	remote: error: update_ref failed for ref 'refs/heads/main': ref updates forbidden inside quarantine environment

   but somehow the push succeeds in spite of this message, and the
   primary and replica both get updated.

   And if I inject an error on push to the replica, by making the
   replica's pre-receive hook fail with nonzero exit status, neither
   primary nor replica is updated and the push fails with an error
   message (`pre-receive hook declined') _and_ nonzero exit status --
   as desired.

   So maybe this actually works, but the error message on _successful_
   pushes is unsettling!

5. Same as (1), but the pre-receive hook assembles a command line of

	exec git send-pack git@xxxxxxxxxxxxxxxxxxx:/repo.git \
		${new0}:${ref0} ${new1}:${ref1} ...

   with all the ref updates passed on stdin (ignoring the old values).

   This seems to work, and it propagates errors injected on push to
   the replica, but it is limited to local or ssh remotes, as far as I
   can tell -- it does not appear that git-send-pack works with custom
   remote transports.

Perhaps using mirror clones is the wrong approach here, and perhaps I
should instead explicitly create tracking branches in the primary that
are only updated if the push succeeds -- but this will still require
getting around the quarantine restrictions on git push in the
pre-receive hook.


Is there a way to achieve this (ideally, with plausible extension to a
three-phase commit protocol) that doesn't trigger unsettling nonfatal
error messages and that works with custom remote transports?





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux