Exemple #1
0
/* Ugly ---> Remove this */
txn_info *init_txn_info(char *database_name, TransactionId gxid)
{
	database_info *database;
	txn_info *cur_txn_info;

	if ((database = find_database_info(database_name)) == NULL)
		return NULL;

	if (database->head_txn_info == NULL)
	{
		database->head_txn_info = database->last_txn_info = (txn_info *)malloc(sizeof(txn_info));
		if (database->head_txn_info == NULL)
			return NULL;
		memset(database->head_txn_info, 0, sizeof(txn_info));
		return database->head_txn_info;
	}
	for(cur_txn_info = database->head_txn_info; cur_txn_info; cur_txn_info = cur_txn_info->next)
	{
		if (cur_txn_info->gxid == gxid)
			return(cur_txn_info);
	}
	cur_txn_info->next = database->last_txn_info = (txn_info *)malloc(sizeof(txn_info));
	if (cur_txn_info->next == NULL)
		return(NULL);
	memset(cur_txn_info->next, 0, sizeof(txn_info));
	if ((cur_txn_info->next->txn_stat = (TXN_STATUS *)malloc(sizeof(TXN_STATUS) * pgxc_clean_node_count)) == NULL)
		return(NULL);
	memset(cur_txn_info->next->txn_stat, 0, sizeof(TXN_STATUS) * pgxc_clean_node_count);
	return cur_txn_info->next;
}
Exemple #2
0
database_info *add_database_info(char *database_name)
{
	database_info *rv;

	if ((rv = find_database_info(database_name)) != NULL)
		return rv;		/* Already in the list */
	rv = malloc(sizeof(database_info));
	if (rv == NULL)
		return NULL;
	rv->next = NULL;
	rv->database_name = strdup(database_name);
	if (rv->database_name == NULL)
	{
		free(rv);
		return NULL;
	}
	rv->head_txn_info = NULL;
	rv->last_txn_info = NULL;
	if (head_database_info == NULL)
	{
		head_database_info = last_database_info = rv;
		return rv;
	}
	else
	{
		last_database_info->next = rv;
		last_database_info = rv;
		return rv;
	}
}
Exemple #3
0
static txn_info *
make_txn_info(char *dbname, TransactionId gxid, char *xid, char *owner)
{
	database_info *dbinfo;
	txn_info *txn;

	if ((dbinfo = find_database_info(dbname)) == NULL)
		dbinfo = add_database_info(dbname);
	txn = (txn_info *)malloc(sizeof(txn_info));
	if (txn == NULL)
		return NULL;
	memset(txn, 0, sizeof(txn_info));
	txn->gxid = gxid;
	txn->xid = strdup(xid);
	if (txn->xid == NULL)
	{
		free(txn);
		return NULL;
	}
	txn->owner = strdup(owner);
	if (txn->owner == NULL)
	{
		free(txn);
		return NULL;
	}
	if (dbinfo->head_txn_info == NULL)
	{
		dbinfo->head_txn_info = dbinfo->last_txn_info = txn;
	}
	else
	{
		dbinfo->last_txn_info->next = txn;
		dbinfo->last_txn_info = txn;
	}
	txn->txn_stat = (TXN_STATUS *)malloc(sizeof(TXN_STATUS) * pgxc_clean_node_count);
	if (txn->txn_stat == NULL)
		return(NULL);
	memset(txn->txn_stat, 0, sizeof(TXN_STATUS) * pgxc_clean_node_count);
	return txn;
}
Exemple #4
0
/*
 *
 * Main
 *
 */
int main(int argc, char *argv[])
{

	/* Should setup pglocale when it is supported by XC core */

	if (argc > 1)
	{
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
		{
			usage();
			exit(0);
		}
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
		{
			showVersion();
			exit(0);
		}
	}
	parse_pgxc_clean_options(argc, argv);

	/*
	 * Check missing arguments
	 */
	if (clean_all_databases == false && head_database_names == NULL)
	{
		fprintf(stderr, "%s: you must specify -a or -d option.\n", progname);
		exit(1);
	}

	/*
	 * Arrange my environment
	 */
	if (output_filename)
	{
		/* Prepare output filename */
		outf = fopen(output_filename, "w");
		if (outf == NULL)
		{
			fprintf(stderr, "%s: Cannot ope output file %s (%s)\n", progname, output_filename, strerror(errno));
			exit(1);
		}
		errf = outf;
	}
	else
	{
		outf = stdout;
		errf = stderr;
	}
	if (coordinator_host == NULL)
	{
		/* Default Coordinator host */
		if ((coordinator_host = getenv("PGHOST")) == NULL)
			coordinator_host = "localhost";
	}
	if (coordinator_port == -1)
	{
		/* Default Coordinator port */
		char *pgport;

		if ((pgport = getenv("PGPORT")) == NULL)
			coordinator_port = DEF_PGPORT;		/* pg_config.h */
		else
			coordinator_port = atoi(pgport);
	}
	if (username == NULL)
		strcpy(password_prompt, "Password: "******"Password for user %s: ", username);
	if (try_password_opt == TRI_YES)
		password = simple_prompt(password_prompt, 100, false);

	if (verbose_opt)
	{
		/* Print environments */
		fprintf(outf, "%s (%s): Cleanup outstanding 2PCs.\n", progname, PG_VERSION);
		/* Target databaess */
		fprintf(outf, "Target databases:");
		if (clean_all_databases)
			fprintf(outf, "(ALL)\n");
		else
		{
			database_names *cur_name;

			for(cur_name = head_database_names; cur_name; cur_name = cur_name->next)
				fprintf(outf, " %s", cur_name->database_name);
			fprintf(outf, "\n");
		}
		/* Username to use */
		fprintf(outf, "Username: %s\n", username ? username : "******");
		/* Status opt */
		fprintf(outf, "Status opt: %s\n", status_opt ? "on" : "off");
		/* No-dlean opt */
		fprintf(outf, "no-clean: %s\n", no_clean_opt ? "on" : "off");
	}

	/* Connect to XC server */
	if (verbose_opt)
	{
		fprintf(outf, "%s: connecting to database \"%s\", host: \"%s\", port: %d\n", 
				progname, 
				clean_all_databases ? "postgres" : head_database_names->database_name,
				coordinator_host, coordinator_port);
	}
	coord_conn = loginDatabase(coordinator_host, coordinator_port, username, password, 
							   clean_all_databases ? "postgres" : head_database_names->database_name,
							   progname, "auto", password_prompt);
	if (verbose_opt)
	{
		fprintf(outf, "%s: connected successfully\n", progname);
	}

	/*
	 * Get my nodename (connected Coordinator)
	 */
	getMyNodename(coord_conn);
	if (verbose_opt)
	{
		fprintf(outf, "%s: Connected to the node \"%s\"\n", progname, my_nodename);
	}

	/*
	 * Get available databases
	 *
	 * pgxc_clean assumes that all the database are available from the connecting Coordinator.
	 * Some (expert) DBA can create a database local to subset of the node by EXECUTE DIRECT.
	 * In this case, DBA may have to clean outstanding 2PC transactions manually or clean
	 * 2PC transactions by connecting pgxc_clean to different Coordinators.
	 *
	 * If such node-subset database is found to be used widely, pgxc_clean may need
	 * an extension to deal with this case.
	 */
	getDatabaseList(coord_conn);
	if (verbose_opt)
	{
		database_info *cur_database;

		fprintf(outf, "%s: Databases visible from the node \"%s\": ", progname, my_nodename);

		if (head_database_info)
		{
			for (cur_database = head_database_info; cur_database; cur_database = cur_database->next)
			{
				fprintf(outf, " \"%s\"", cur_database->database_name);
			}
			fputc('\n', outf);
		}
	}

	/*
	 * Get list of Coordinators
	 *
	 * As in the case of database, we clean transactions in visible nodes from the
	 * connecting Coordinator. DBA can also setup different node configuration
	 * at different Coordinators. In this case, DBA should be careful to choose
	 * appropriate Coordinator to clean up transactions.
	 */
	getNodeList(coord_conn);
	if (verbose_opt)
	{
		int ii;

		fprintf(outf, "%s: Node list visible from the node \"%s\"\n", progname, my_nodename);

		for (ii = 0; ii < pgxc_clean_node_count; ii++)
		{
			fprintf(outf, "Name: %s, host: %s, port: %d, type: %s\n",
					pgxc_clean_node_info[ii].node_name,
					pgxc_clean_node_info[ii].host,
					pgxc_clean_node_info[ii].port,
					pgxc_clean_node_info[ii].type == NODE_TYPE_COORD ? "coordinator" : "datanode");
		}
	}

	/*
	 * Get list of prepared statement
	 */
	getPreparedTxnList(coord_conn);

	/*
	 * Check if there're any 2PC candidate to recover
	 */
	if (!check2PCExists())
	{
		fprintf(errf, "%s: There's no prepared 2PC in this cluster.  Exiting.\n", progname);
		exit(0);
	}


	/*
	 * Check status of each prepared transaction.  To do this, look into
	 * nodes where the transaction is not recorded as "prepared".
	 * Possible status are unknown (prepare has not been issued), committed or
	 * aborted.
	 */
	getTxnInfoOnOtherNodesAll(coord_conn);
	if (verbose_opt)
	{
		/* Print all the prepared transaction list */
		database_info *cur_db;

		fprintf(outf, "%s: 2PC transaction list.\n", progname);
		for (cur_db = head_database_info; cur_db; cur_db = cur_db->next)
		{
			txn_info *txn;

			fprintf(outf, "Database: \"%s\":\n", cur_db->database_name);

			for (txn = cur_db->head_txn_info; txn; txn = txn->next)
			{
				int ii;

				fprintf(outf, "    gxid: %d, xid: \"%s\", owner: %s\n", txn->gxid, txn->xid, txn->owner);
				for (ii = 0; ii < pgxc_clean_node_count; ii++)
				{
					fprintf(outf, "        node: %s, status: %s\n",
							pgxc_clean_node_info[ii].node_name,
							str_txn_stat(txn->txn_stat[ii]));
				}
			}
		}
	}

	/*
	 * Then disconnect from the database.
	 * I need to login to specified databases which 2PC is issued for.  Again, we assume
	 * that all the prepare is issued against the same database in each node, which
	 * current Coordinator does and there seems to be no way to violate this assumption.
	 */
	if (verbose_opt)
	{
		fprintf(outf, "%s: disconnecting\n", progname);
	}
	PQfinish(coord_conn);

	/*
	 * If --no-clean option is specified, we exit here.
	 */
	if (no_clean_opt)
	{
		fprintf(outf, "--no-clean opt is specified. Exiting.\n");
		exit(0);
	}

	/*
	 * Recover 2PC for specified databases
	 */
	if (clean_all_databases)
	{
		database_info *cur_database_info;

		for(cur_database_info = head_database_info; cur_database_info; cur_database_info = cur_database_info->next)
		{
			recover2PCForDatabase(cur_database_info);
		}
	}
	else
	{
		database_info *cur_database_info;
		database_names *cur_database_name;

		for(cur_database_name = head_database_names; cur_database_name; cur_database_name = cur_database_name->next)
		{
			cur_database_info = find_database_info(cur_database_name->database_name);
			if (cur_database_info)
			{
				recover2PCForDatabase(cur_database_info);
			}
		}
	}
	exit(0);
}