<commit_before><?php

final class PhabricatorRepositoryWorkingCopyVersion
  extends PhabricatorRepositoryDAO {

  protected $repositoryPHID;
  protected $devicePHID;
  protected $repositoryVersion;
  protected $isWriting;
  protected $lockOwner;
  protected $writeProperties;

  protected function getConfiguration() {
    return array(
      self::CONFIG_TIMESTAMPS => false,
      self::CONFIG_COLUMN_SCHEMA => array(
        'repositoryVersion' => 'uint32',
        'isWriting' => 'bool',
        'writeProperties' => 'text?',
        'lockOwner' => 'text255?',
      ),
      self::CONFIG_KEY_SCHEMA => array(
        'key_workingcopy' => array(
          'columns' => array('repositoryPHID', 'devicePHID'),
          'unique' => true,
        ),
      ),
    ) + parent::getConfiguration();
  }

  public function getWriteProperty($key, $default = null) {
    // The "writeProperties" don't currently get automatically serialized or
    // deserialized. Perhaps they should.
    try {
      $properties = phutil_json_decode($this->writeProperties);
      return idx($properties, $key, $default);
    } catch (Exception $ex) {
      return null;
    }
  }

  public static function loadVersions($repository_phid) {
    $version = new self();
    $conn_w = $version->establishConnection('w');
    $table = $version->getTableName();

    // This is a normal read, but force it to come from the master.
    $rows = queryfx_all(
      $conn_w,
      'SELECT * FROM %T WHERE repositoryPHID = %s',
      $table,
      $repository_phid);

    return $version->loadAllFromArray($rows);
  }

  public static function loadWriter($repository_phid) {
    $version = new self();
    $conn_w = $version->establishConnection('w');
    $table = $version->getTableName();

    // We're forcing this read to go to the master.
    $row = queryfx_one(
      $conn_w,
      'SELECT * FROM %T WHERE repositoryPHID = %s AND isWriting = 1
        LIMIT 1',
      $table,
      $repository_phid);

    if (!$row) {
      return null;
    }

    return $version->loadFromArray($row);
  }


  public static function getReadLock($repository_phid, $device_phid) {
    $repository_hash = PhabricatorHash::digestForIndex($repository_phid);
    $device_hash = PhabricatorHash::digestForIndex($device_phid);
    $lock_key = "repo.read({$repository_hash}, {$device_hash})";

    return PhabricatorGlobalLock::newLock($lock_key);
  }

  public static function getWriteLock($repository_phid) {
    $repository_hash = PhabricatorHash::digestForIndex($repository_phid);
    $lock_key = "repo.write({$repository_hash})";

    return PhabricatorGlobalLock::newLock($lock_key);
  }


  /**
   * Before a write, set the "isWriting" flag.
   *
   * This allows us to detect when we lose a node partway through a write and
   * may have committed and acknowledged a write on a node that lost the lock
   * partway through the write and is no longer reachable.
   *<commit_msg>Parameterize the repository read and write locks<commit_after><?php

final class PhabricatorRepositoryWorkingCopyVersion
  extends PhabricatorRepositoryDAO {

  protected $repositoryPHID;
  protected $devicePHID;
  protected $repositoryVersion;
  protected $isWriting;
  protected $lockOwner;
  protected $writeProperties;

  protected function getConfiguration() {
    return array(
      self::CONFIG_TIMESTAMPS => false,
      self::CONFIG_COLUMN_SCHEMA => array(
        'repositoryVersion' => 'uint32',
        'isWriting' => 'bool',
        'writeProperties' => 'text?',
        'lockOwner' => 'text255?',
      ),
      self::CONFIG_KEY_SCHEMA => array(
        'key_workingcopy' => array(
          'columns' => array('repositoryPHID', 'devicePHID'),
          'unique' => true,
        ),
      ),
    ) + parent::getConfiguration();
  }

  public function getWriteProperty($key, $default = null) {
    // The "writeProperties" don't currently get automatically serialized or
    // deserialized. Perhaps they should.
    try {
      $properties = phutil_json_decode($this->writeProperties);
      return idx($properties, $key, $default);
    } catch (Exception $ex) {
      return null;
    }
  }

  public static function loadVersions($repository_phid) {
    $version = new self();
    $conn_w = $version->establishConnection('w');
    $table = $version->getTableName();

    // This is a normal read, but force it to come from the master.
    $rows = queryfx_all(
      $conn_w,
      'SELECT * FROM %T WHERE repositoryPHID = %s',
      $table,
      $repository_phid);

    return $version->loadAllFromArray($rows);
  }

  public static function loadWriter($repository_phid) {
    $version = new self();
    $conn_w = $version->establishConnection('w');
    $table = $version->getTableName();

    // We're forcing this read to go to the master.
    $row = queryfx_one(
      $conn_w,
      'SELECT * FROM %T WHERE repositoryPHID = %s AND isWriting = 1
        LIMIT 1',
      $table,
      $repository_phid);

    if (!$row) {
      return null;
    }

    return $version->loadFromArray($row);
  }


  public static function getReadLock($repository_phid, $device_phid) {
    $parameters = array(
      'repositoryPHID' => $repository_phid,
      'devicePHID' => $device_phid,
    );

    return PhabricatorGlobalLock::newLock('repo.read', $parameters);
  }

  public static function getWriteLock($repository_phid) {
    $parameters = array(
      'repositoryPHID' => $repository_phid,
    );

    return PhabricatorGlobalLock::newLock('repo.write', $parameters);
  }


  /**
   * Before a write, set the "isWriting" flag.
   *
   * This allows us to detect when we lose a node partway through a write and
   * may have committed and acknowledged a write on a node that lost the lock
   * partway through the write and is no longer reachable.
   *
{'fraction_non_alphanumeric': 0.11902708297395205, 'fraction_numerical': 0.002415042263239607, 'mean_word_length': 3.2136627906976742, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6003025', 'n_tokens_mistral': 1764, 'n_tokens_neox': 1713, 'n_words': 451}