Page 1 of 1

How to overwrite a file atomically?

Posted: Fri Sep 08, 2017 2:05 pm
by RyanCaleb
I have a php script that is executed quite often via ajax and its purpose is to record the time a user was last 'seen' on the site (this is for the purpose of availability status in a chat). I decided to write this information to small files because it might be too heavy for the database.
Please Help !
Thanks !
I didn't find the right solution from the internet.
References:

https://www.sitepoint.com/community/t/h ... /268982/21 [link fixed->kaspir]

[LINK REMOVED->axew3]

Re: How to overwrite a file atomically?

Posted: Fri Sep 08, 2017 7:35 pm
by axew3
first link return
Oops! That page doesn’t exist or is private.
while the second, redirect to a page where there are NOT FREE tutorials. So removed.

Re: How to overwrite a file atomically?

Posted: Sun Sep 10, 2017 11:49 am
by kaspir
phpBB forums stores this very same data in mysqli. Why would you complicate things? (dont answer that! :D )

I read your conversation in sitepoint. Are you aware of this?

Code: Select all

Benchmark below:

file_put_contents() for 1,000,000 writes - average of 3 benchmarks:

real 0m3.932s
user 0m2.487s
sys 0m1.437s

fopen() fwrite() for 1,000,000 writes, fclose() -  average of 3 benchmarks:

real 0m2.265s
user 0m1.819s
sys 0m0.445s
because when you say atomically, you want to correct these blank results you're getting by making it faster (or slower with cache timer), right?
I'm not too sure if faster is > safer. But, if you are making multiple writes to same file, would it be better to do it yourself using fopen(), etc?

Re: How to overwrite a file atomically?

Posted: Sun Sep 10, 2017 2:18 pm
by axew3
:) Hello all! i can see the post now! After Kaspir's answer i've take a re-look ... now it display. It was (may) looking as spam to me, in first instance...

I've a little class that i've write for the WordPress Dropbox plugin, that may will help (or not, i'm not sure about the meaning of all your topic at sitepoint.com), but may this will result useful, also for someone else.

I read on an answer:
1. Does each user have a dedicated file for that data?
2. When are you writing to that file and why do you run into concurrent write scenarios?
would you like a single file, that for each line, containing the data you need for a session/user, or multiple files, one file for each?

The following code is about one file for each, not complete code (some points maybe will be not so clear due to the fact you do not know form where these vars are coming) but you'll take maybe a suggestion in this particular code parts (if i've +- understand your trouble, maybe not):

Code: Select all

	$f = @file_get_contents($this->cache_folder . $w3all_dbx_ucache[0]['filename'] . ".txt");
		if(!$f):
		 return false;
		else : return $f;
		endif;
	}
AND

Code: Select all

	 if(!@file_put_contents($this->cache_file, $restot_items)){
	  		$error = w3all_common_errors(0);
		  	 if( is_wp_error( $error ) ) {
          $error->get_error_message();
         }
    	}
  } else {
  	       $this->cache_file = $this->cache_folder . $w3all_dbx_ucache[0]['filename'] . ".txt";
  	       file_put_contents($this->cache_file, $restot_items);
    }
This is the little complete class i've write time ago
(which concept/code can be (quite) easily adapted for any other use):

Code: Select all

// This is a simply w3all cache class!

class w3all_cache {

	public $cache_folder = WP3ALL_DROPBOX_PLUGIN_DIR . 'cache/';
	
public function switch_cache() {
		global $w3all_dbx_ucache,$w3all_dropb_cache_time;

		$m = isset($w3all_dbx_ucache[0]['time']) ? $w3all_dbx_ucache[0]['time'] : 0;

		if((time() - $w3all_dropb_cache_time) > $m){
		return self::do_cache(); 

		} else {
			 return self::read_cache();
		  }
	}

	/**
	 * Write the cache file for WP user
	 */
public function do_cache($var = '') {

		global $restot_items, $g_user_id, $w3all_dbx_ucache;

if(empty($restot_items) && $var != 1): 
 return 1;
endif;

$rt = $restot_items;
$restot_items = json_encode($restot_items, JSON_UNESCAPED_SLASHES); // as php 5.4 or >

 if(!isset($w3all_dbx_ucache[0]['filename'])){
		 $filename = md5(microtime() . rand(5, 15));
	 $this->cache_file = $this->cache_folder . $filename . ".txt";
		 if(!@file_put_contents($this->cache_file, $restot_items)){
	  		$error = w3all_common_errors(0);
		  	 if( is_wp_error( $error ) ) {
          $error->get_error_message();
         }
    	}
  } else {
  	       $this->cache_file = $this->cache_folder . $w3all_dbx_ucache[0]['filename'] . ".txt";
  	       file_put_contents($this->cache_file, $restot_items);
    }
		
		$fname = (isset($w3all_dbx_ucache[0]['filename'])) ? $w3all_dbx_ucache[0]['filename'] : $filename;
		$data = array('time' => time(), 'filename' => $fname);
		update_user_meta( $g_user_id, 'wp_w3all_dbx_ucache', $data, '' );
		
	 return $rt;
		
	}

	/**
	 * Read cache
	 */
	public function read_cache() {
		global $w3all_dbx_ucache;
   
		$f = @file_get_contents($this->cache_folder . $w3all_dbx_ucache[0]['filename'] . ".txt");
		if(!$f):
		 return false;
		else : return $f;
		endif;
	}

} // end class

$cache = new w3all_cache;
$cache_ck = $cache->switch_cache();