github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/lib/pq/notify_test.go (about)

     1  package pq
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"runtime"
     9  	"sync"
    10  	"sync/atomic"
    11  	"testing"
    12  	"time"
    13  )
    14  
    15  var errNilNotification = errors.New("nil notification")
    16  
    17  func expectNotification(t *testing.T, ch <-chan *Notification, relname string, extra string) error {
    18  	select {
    19  	case n := <-ch:
    20  		if n == nil {
    21  			return errNilNotification
    22  		}
    23  		if n.Channel != relname || n.Extra != extra {
    24  			return fmt.Errorf("unexpected notification %v", n)
    25  		}
    26  		return nil
    27  	case <-time.After(1500 * time.Millisecond):
    28  		return fmt.Errorf("timeout")
    29  	}
    30  }
    31  
    32  func expectNoNotification(t *testing.T, ch <-chan *Notification) error {
    33  	select {
    34  	case n := <-ch:
    35  		return fmt.Errorf("unexpected notification %v", n)
    36  	case <-time.After(100 * time.Millisecond):
    37  		return nil
    38  	}
    39  }
    40  
    41  func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error {
    42  	select {
    43  	case e := <-eventch:
    44  		if e != et {
    45  			return fmt.Errorf("unexpected event %v", e)
    46  		}
    47  		return nil
    48  	case <-time.After(1500 * time.Millisecond):
    49  		panic("expectEvent timeout")
    50  	}
    51  }
    52  
    53  func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error {
    54  	select {
    55  	case e := <-eventch:
    56  		return fmt.Errorf("unexpected event %v", e)
    57  	case <-time.After(100 * time.Millisecond):
    58  		return nil
    59  	}
    60  }
    61  
    62  func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) {
    63  	datname := os.Getenv("PGDATABASE")
    64  	sslmode := os.Getenv("PGSSLMODE")
    65  
    66  	if datname == "" {
    67  		os.Setenv("PGDATABASE", "pqgotest")
    68  	}
    69  
    70  	if sslmode == "" {
    71  		os.Setenv("PGSSLMODE", "disable")
    72  	}
    73  
    74  	notificationChan := make(chan *Notification)
    75  	l, err := NewListenerConn("", notificationChan)
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  
    80  	return l, notificationChan
    81  }
    82  
    83  func TestNewListenerConn(t *testing.T) {
    84  	l, _ := newTestListenerConn(t)
    85  
    86  	defer l.Close()
    87  }
    88  
    89  func TestConnListen(t *testing.T) {
    90  	l, channel := newTestListenerConn(t)
    91  
    92  	defer l.Close()
    93  
    94  	db := openTestConn(t)
    95  	defer db.Close()
    96  
    97  	ok, err := l.Listen("notify_test")
    98  	if !ok || err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	_, err = db.Exec("NOTIFY notify_test")
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  
   107  	err = expectNotification(t, channel, "notify_test", "")
   108  	if err != nil {
   109  		t.Fatal(err)
   110  	}
   111  }
   112  
   113  func TestConnUnlisten(t *testing.T) {
   114  	l, channel := newTestListenerConn(t)
   115  
   116  	defer l.Close()
   117  
   118  	db := openTestConn(t)
   119  	defer db.Close()
   120  
   121  	ok, err := l.Listen("notify_test")
   122  	if !ok || err != nil {
   123  		t.Fatal(err)
   124  	}
   125  
   126  	_, err = db.Exec("NOTIFY notify_test")
   127  
   128  	err = expectNotification(t, channel, "notify_test", "")
   129  	if err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	ok, err = l.Unlisten("notify_test")
   134  	if !ok || err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	_, err = db.Exec("NOTIFY notify_test")
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  
   143  	err = expectNoNotification(t, channel)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  }
   148  
   149  func TestConnUnlistenAll(t *testing.T) {
   150  	l, channel := newTestListenerConn(t)
   151  
   152  	defer l.Close()
   153  
   154  	db := openTestConn(t)
   155  	defer db.Close()
   156  
   157  	ok, err := l.Listen("notify_test")
   158  	if !ok || err != nil {
   159  		t.Fatal(err)
   160  	}
   161  
   162  	_, err = db.Exec("NOTIFY notify_test")
   163  
   164  	err = expectNotification(t, channel, "notify_test", "")
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  
   169  	ok, err = l.UnlistenAll()
   170  	if !ok || err != nil {
   171  		t.Fatal(err)
   172  	}
   173  
   174  	_, err = db.Exec("NOTIFY notify_test")
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  
   179  	err = expectNoNotification(t, channel)
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  }
   184  
   185  func TestConnClose(t *testing.T) {
   186  	l, _ := newTestListenerConn(t)
   187  	defer l.Close()
   188  
   189  	err := l.Close()
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  	err = l.Close()
   194  	if err != errListenerConnClosed {
   195  		t.Fatalf("expected errListenerConnClosed; got %v", err)
   196  	}
   197  }
   198  
   199  func TestConnPing(t *testing.T) {
   200  	l, _ := newTestListenerConn(t)
   201  	defer l.Close()
   202  	err := l.Ping()
   203  	if err != nil {
   204  		t.Fatal(err)
   205  	}
   206  	err = l.Close()
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	err = l.Ping()
   211  	if err != errListenerConnClosed {
   212  		t.Fatalf("expected errListenerConnClosed; got %v", err)
   213  	}
   214  }
   215  
   216  // Test for deadlock where a query fails while another one is queued
   217  func TestConnExecDeadlock(t *testing.T) {
   218  	l, _ := newTestListenerConn(t)
   219  	defer l.Close()
   220  
   221  	var wg sync.WaitGroup
   222  	wg.Add(2)
   223  
   224  	go func() {
   225  		l.ExecSimpleQuery("SELECT pg_sleep(60)")
   226  		wg.Done()
   227  	}()
   228  	runtime.Gosched()
   229  	go func() {
   230  		l.ExecSimpleQuery("SELECT 1")
   231  		wg.Done()
   232  	}()
   233  	// give the two goroutines some time to get into position
   234  	runtime.Gosched()
   235  	// calls Close on the net.Conn; equivalent to a network failure
   236  	l.Close()
   237  
   238  	var done int32 = 0
   239  	go func() {
   240  		time.Sleep(10 * time.Second)
   241  		if atomic.LoadInt32(&done) != 1 {
   242  			panic("timed out")
   243  		}
   244  	}()
   245  	wg.Wait()
   246  	atomic.StoreInt32(&done, 1)
   247  }
   248  
   249  // Test for ListenerConn being closed while a slow query is executing
   250  func TestListenerConnCloseWhileQueryIsExecuting(t *testing.T) {
   251  	l, _ := newTestListenerConn(t)
   252  	defer l.Close()
   253  
   254  	var wg sync.WaitGroup
   255  	wg.Add(1)
   256  
   257  	go func() {
   258  		sent, err := l.ExecSimpleQuery("SELECT pg_sleep(60)")
   259  		if sent {
   260  			panic("expected sent=false")
   261  		}
   262  		// could be any of a number of errors
   263  		if err == nil {
   264  			panic("expected error")
   265  		}
   266  		wg.Done()
   267  	}()
   268  	// give the above goroutine some time to get into position
   269  	runtime.Gosched()
   270  	err := l.Close()
   271  	if err != nil {
   272  		t.Fatal(err)
   273  	}
   274  	var done int32 = 0
   275  	go func() {
   276  		time.Sleep(10 * time.Second)
   277  		if atomic.LoadInt32(&done) != 1 {
   278  			panic("timed out")
   279  		}
   280  	}()
   281  	wg.Wait()
   282  	atomic.StoreInt32(&done, 1)
   283  }
   284  
   285  func TestNotifyExtra(t *testing.T) {
   286  	db := openTestConn(t)
   287  	defer db.Close()
   288  
   289  	if getServerVersion(t, db) < 90000 {
   290  		t.Skip("skipping NOTIFY payload test since the server does not appear to support it")
   291  	}
   292  
   293  	l, channel := newTestListenerConn(t)
   294  	defer l.Close()
   295  
   296  	ok, err := l.Listen("notify_test")
   297  	if !ok || err != nil {
   298  		t.Fatal(err)
   299  	}
   300  
   301  	_, err = db.Exec("NOTIFY notify_test, 'something'")
   302  	if err != nil {
   303  		t.Fatal(err)
   304  	}
   305  
   306  	err = expectNotification(t, channel, "notify_test", "something")
   307  	if err != nil {
   308  		t.Fatal(err)
   309  	}
   310  }
   311  
   312  // create a new test listener and also set the timeouts
   313  func newTestListenerTimeout(t *testing.T, min time.Duration, max time.Duration) (*Listener, <-chan ListenerEventType) {
   314  	datname := os.Getenv("PGDATABASE")
   315  	sslmode := os.Getenv("PGSSLMODE")
   316  
   317  	if datname == "" {
   318  		os.Setenv("PGDATABASE", "pqgotest")
   319  	}
   320  
   321  	if sslmode == "" {
   322  		os.Setenv("PGSSLMODE", "disable")
   323  	}
   324  
   325  	eventch := make(chan ListenerEventType, 16)
   326  	l := NewListener("", min, max, func(t ListenerEventType, err error) { eventch <- t })
   327  	err := expectEvent(t, eventch, ListenerEventConnected)
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	return l, eventch
   332  }
   333  
   334  func newTestListener(t *testing.T) (*Listener, <-chan ListenerEventType) {
   335  	return newTestListenerTimeout(t, time.Hour, time.Hour)
   336  }
   337  
   338  func TestListenerListen(t *testing.T) {
   339  	l, _ := newTestListener(t)
   340  	defer l.Close()
   341  
   342  	db := openTestConn(t)
   343  	defer db.Close()
   344  
   345  	err := l.Listen("notify_listen_test")
   346  	if err != nil {
   347  		t.Fatal(err)
   348  	}
   349  
   350  	_, err = db.Exec("NOTIFY notify_listen_test")
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  
   355  	err = expectNotification(t, l.Notify, "notify_listen_test", "")
   356  	if err != nil {
   357  		t.Fatal(err)
   358  	}
   359  }
   360  
   361  func TestListenerUnlisten(t *testing.T) {
   362  	l, _ := newTestListener(t)
   363  	defer l.Close()
   364  
   365  	db := openTestConn(t)
   366  	defer db.Close()
   367  
   368  	err := l.Listen("notify_listen_test")
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  
   373  	_, err = db.Exec("NOTIFY notify_listen_test")
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  
   378  	err = l.Unlisten("notify_listen_test")
   379  	if err != nil {
   380  		t.Fatal(err)
   381  	}
   382  
   383  	err = expectNotification(t, l.Notify, "notify_listen_test", "")
   384  	if err != nil {
   385  		t.Fatal(err)
   386  	}
   387  
   388  	_, err = db.Exec("NOTIFY notify_listen_test")
   389  	if err != nil {
   390  		t.Fatal(err)
   391  	}
   392  
   393  	err = expectNoNotification(t, l.Notify)
   394  	if err != nil {
   395  		t.Fatal(err)
   396  	}
   397  }
   398  
   399  func TestListenerUnlistenAll(t *testing.T) {
   400  	l, _ := newTestListener(t)
   401  	defer l.Close()
   402  
   403  	db := openTestConn(t)
   404  	defer db.Close()
   405  
   406  	err := l.Listen("notify_listen_test")
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  
   411  	_, err = db.Exec("NOTIFY notify_listen_test")
   412  	if err != nil {
   413  		t.Fatal(err)
   414  	}
   415  
   416  	err = l.UnlistenAll()
   417  	if err != nil {
   418  		t.Fatal(err)
   419  	}
   420  
   421  	err = expectNotification(t, l.Notify, "notify_listen_test", "")
   422  	if err != nil {
   423  		t.Fatal(err)
   424  	}
   425  
   426  	_, err = db.Exec("NOTIFY notify_listen_test")
   427  	if err != nil {
   428  		t.Fatal(err)
   429  	}
   430  
   431  	err = expectNoNotification(t, l.Notify)
   432  	if err != nil {
   433  		t.Fatal(err)
   434  	}
   435  }
   436  
   437  func TestListenerFailedQuery(t *testing.T) {
   438  	l, eventch := newTestListener(t)
   439  	defer l.Close()
   440  
   441  	db := openTestConn(t)
   442  	defer db.Close()
   443  
   444  	err := l.Listen("notify_listen_test")
   445  	if err != nil {
   446  		t.Fatal(err)
   447  	}
   448  
   449  	_, err = db.Exec("NOTIFY notify_listen_test")
   450  	if err != nil {
   451  		t.Fatal(err)
   452  	}
   453  
   454  	err = expectNotification(t, l.Notify, "notify_listen_test", "")
   455  	if err != nil {
   456  		t.Fatal(err)
   457  	}
   458  
   459  	// shouldn't cause a disconnect
   460  	ok, err := l.cn.ExecSimpleQuery("SELECT error")
   461  	if !ok {
   462  		t.Fatalf("could not send query to server: %v", err)
   463  	}
   464  	_, ok = err.(PGError)
   465  	if !ok {
   466  		t.Fatalf("unexpected error %v", err)
   467  	}
   468  	err = expectNoEvent(t, eventch)
   469  	if err != nil {
   470  		t.Fatal(err)
   471  	}
   472  
   473  	// should still work
   474  	_, err = db.Exec("NOTIFY notify_listen_test")
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  
   479  	err = expectNotification(t, l.Notify, "notify_listen_test", "")
   480  	if err != nil {
   481  		t.Fatal(err)
   482  	}
   483  }
   484  
   485  func TestListenerReconnect(t *testing.T) {
   486  	l, eventch := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
   487  	defer l.Close()
   488  
   489  	db := openTestConn(t)
   490  	defer db.Close()
   491  
   492  	err := l.Listen("notify_listen_test")
   493  	if err != nil {
   494  		t.Fatal(err)
   495  	}
   496  
   497  	_, err = db.Exec("NOTIFY notify_listen_test")
   498  	if err != nil {
   499  		t.Fatal(err)
   500  	}
   501  
   502  	err = expectNotification(t, l.Notify, "notify_listen_test", "")
   503  	if err != nil {
   504  		t.Fatal(err)
   505  	}
   506  
   507  	// kill the connection and make sure it comes back up
   508  	ok, err := l.cn.ExecSimpleQuery("SELECT pg_terminate_backend(pg_backend_pid())")
   509  	if ok {
   510  		t.Fatalf("could not kill the connection: %v", err)
   511  	}
   512  	if err != io.EOF {
   513  		t.Fatalf("unexpected error %v", err)
   514  	}
   515  	err = expectEvent(t, eventch, ListenerEventDisconnected)
   516  	if err != nil {
   517  		t.Fatal(err)
   518  	}
   519  	err = expectEvent(t, eventch, ListenerEventReconnected)
   520  	if err != nil {
   521  		t.Fatal(err)
   522  	}
   523  
   524  	// should still work
   525  	_, err = db.Exec("NOTIFY notify_listen_test")
   526  	if err != nil {
   527  		t.Fatal(err)
   528  	}
   529  
   530  	// should get nil after Reconnected
   531  	err = expectNotification(t, l.Notify, "", "")
   532  	if err != errNilNotification {
   533  		t.Fatal(err)
   534  	}
   535  
   536  	err = expectNotification(t, l.Notify, "notify_listen_test", "")
   537  	if err != nil {
   538  		t.Fatal(err)
   539  	}
   540  }
   541  
   542  func TestListenerClose(t *testing.T) {
   543  	l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
   544  	defer l.Close()
   545  
   546  	err := l.Close()
   547  	if err != nil {
   548  		t.Fatal(err)
   549  	}
   550  	err = l.Close()
   551  	if err != errListenerClosed {
   552  		t.Fatalf("expected errListenerClosed; got %v", err)
   553  	}
   554  }
   555  
   556  func TestListenerPing(t *testing.T) {
   557  	l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
   558  	defer l.Close()
   559  
   560  	err := l.Ping()
   561  	if err != nil {
   562  		t.Fatal(err)
   563  	}
   564  
   565  	err = l.Close()
   566  	if err != nil {
   567  		t.Fatal(err)
   568  	}
   569  
   570  	err = l.Ping()
   571  	if err != errListenerClosed {
   572  		t.Fatalf("expected errListenerClosed; got %v", err)
   573  	}
   574  }